I am new to React (and still new to JS too), and i am trying to build my first React project. I am fetching an API , rendering some items, and building a Search Bar that filters out the items rendered.
My filtering function is more or less working, and inside of it, i store the filtered results in let result
, but How i should access those results from the return part (JSX area, i think) to loop over them?
This is my code :
import React, { useState, useEffect } from "react";
import ListItem from "./ListItem";
const List = () => {
const [data, setData] = useState();
const [input, setInput] = useState("");
const onInputChange = (event) => {
setInput(event.target.value);
const value = event.target.value.toLowerCase();
let result = [];
result = data.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
);
setInput(result);
};
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://rickandmortyapi.com/api/character/"
);
const obj = await response.json();
setData(obj.results);
};
getData();
}, []);
return (
<div>
<input type="text" name={input} onChange={onInputChange}></input>
{data &&
data.map((item) => {
return <ListItem key={item.id} character={item} />;
})}
</div>
);
};
export default List;
So far, I can only loop over input
which contains the results
, like this input && input.map((item)
, but that gives me an empty array when the page is loaded , until i make a search.
You need to use a variable to store data after filter:
const [data, setData] = useState([]);
const onInputChange = (event) => {
setInput(event.target.value);
};
const result = data.filter((item) =>
item.name.toLowerCase().includes(input.toLowerCase())
);
return (
...
{result?.map((item) => {
<ListItem key={item.id} character={item} />;
})}
...
)
You just initialise input as a string so just keep input for keeping input value not result data. You can create another state for keeping result OR put result data back on Data variable.
Here I am showing you to keep result data separate.
import React, { useState, useEffect } from "react";
import ListItem from "./ListItem";
const List = () => {
const [data, setData] = useState();
const [searchResult, setSearchResult] = useState();
const [input, setInput] = useState("");
const onInputChange = (event) => {
setInput(event.target.value);
const value = event.target.value.toLowerCase();
let result = [];
result = data.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
);
setSearchResult(result);
};
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://rickandmortyapi.com/api/character/"
);
const obj = await response.json();
setData(obj.results);
};
getData();
}, []);
return (
<div>
<input type="text" name={input} onChange={onInputChange}></input>
{input===""? data &&
data.map((item) => {
return <ListItem key={item.id} character={item} />;
}):
searchResult &&
searchResult.map((item) => {
return <ListItem key={item.id} character={item} />;
})
}
</div>
);
};
export default List;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This is separating your original data and search result different.
One possible solution would be to filter while rendering,
In this scenario you would only need to save the the input value (onInputChange):
const onInputChange = (event) => {
setInput(event.target.value);
};
Then while rendering you would need to add the filtering logic:
{ // if input is not empty
data
.filter(item => item.name.includes(input.toLowerCase()))
.map((item) => {
return <ListItem key={item.id} character={item} />;
})