curriculum/challenges/english/blocks/workshop-fruit-search-app/68140ab94fd0ea19c62ca87e.md
Below your timeoutId variable, return a cleanup function that clears the timeout using clearTimeout(timeoutId). This prevents multiple delayed fetches from stacking up. Make sure your cleanup function is an arrow function with an implicit return.
And with that your Fruit Search app is complete! Try out your fruit search app by typing in the name of a fruit like apple or pear to see the results appear.
You should return an arrow function that calls clearTimeout(timeoutId) at the end of the useEffect hook.
assert.match(
code,
/return\s*\(\s*\)\s*=>\s*clearTimeout\s*\(\s*timeoutId\s*\)/s
);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Fruits Search</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { FruitsSearch } from './index.jsx';
ReactDOM.createRoot(document.getElementById('root')).render(<FruitsSearch />);
</script>
</body>
</html>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#search-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#search-input {
padding: 10px;
width: 80%;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
#results {
text-align: left;
max-height: 150px;
overflow-y: auto;
}
.result-item {
padding: 5px;
border-bottom: 1px solid #ddd;
}
const { useState, useEffect } = React;
export function FruitsSearch() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
function handleSubmit(e) {
e.preventDefault();
}
useEffect(() => {
if (query.trim() === '') {
setResults([]);
return;
}
--fcc-editable-region--
const timeoutId = setTimeout(async () => {
try {
const response = await fetch(`https://fruit-search.freecodecamp.rocks/api/fruits?q=${query}`);
const data = await response.json();
setResults(data.map(fruit => fruit.name));
} catch (error) {
console.error("Error fetching data:", error);
}
}, 700);
}, [query]);
--fcc-editable-region--
return (
<div id="search-container">
<form onSubmit={handleSubmit}>
<label htmlFor="search-input">Search for fruits:</label>
<input
id="search-input"
type="search"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
<div id="results">
{results.length > 0 ? (
results.map(item => (
<p key={item} className="result-item">{item}</p>
))
) : (
<p>No results found</p>
)}
</div>
</div>
);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Fruits Search</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { FruitsSearch } from './index.jsx';
ReactDOM.createRoot(document.getElementById('root')).render(<FruitsSearch />);
</script>
</body>
</html>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#search-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#search-input {
padding: 10px;
width: 80%;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
#results {
text-align: left;
max-height: 150px;
overflow-y: auto;
}
.result-item {
padding: 5px;
border-bottom: 1px solid #ddd;
}
const { useState, useEffect } = React;
export function FruitsSearch() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
if (query.trim() === '') {
setResults([]);
return;
}
const timeoutId = setTimeout(async () => {
try {
const response = await fetch(`https://fruit-search.freecodecamp.rocks/api/fruits?q=${query}`);
const data = await response.json();
setResults(data.map(fruit => fruit.name));
} catch (error) {
console.error("Error fetching data:", error);
}
}, 700);
return () => clearTimeout(timeoutId);
}, [query]);
return (
<div id="search-container">
<form>
<label htmlFor="search-input">Search for fruits:</label>
<input
id="search-input"
type="search"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
<div id="results">
{results.length > 0 ? (
results.map(item => (
<p key={item} className="result-item">{item}</p>
))
) : (
<p>No results found</p>
)}
</div>
</div>
);
}