Reputation: 84
Experience: I am a total beginner in React.
What I am trying to learn: Hooks (useState
) - but I do not know how to update the state and rerender the view with this. As far as I understood React does not rerender the view if the updated state is somewhat similar to the last one... After googling, I tried to copy the state and update it somehow, but I am missing something, and I do not know what.
What I am trying to do in the project: I have a list of countries I want to filter through when the user selects a region from a dropdown. This is the function that gets fired when the selection happens, along with comments that I hope explain what I am trying to do:
const change = event => {
//copy the `data` state (which has a list of all the countries)
let newData = [...data];
console.log(newData);
//filter through the countries list to get only those with the selected region
let filtered = newData.filter(obj => obj.region === event.target.value);
console.log(filtered);
//change the countries list with the filtered one, and rerender the view
setData([data, newData]);
console.log(data);
};
You can find the file and the code in question HERE (scroll down to get to the change
function)
Select a region from the 'Fitler by region dropdown'
See the errors/outputs in the console
Upvotes: 1
Views: 1980
Reputation: 11760
You are updating the state to an array of objects and the last item will be the filtered list
Instead, pass in a single array that holds the filtered countries.
Note that your state will be lost the second time you select a different region because you are modifying the entire collection of countries.
setData(data.filter(obj => obj.region === event.target.value))
So what you can we to avoid losing the state?
We can filter the list based on the selected region.
Added comments where i changed the code
export default function CountriesList() {
const [data, setData] = useState([]);
const [distinctRegions, setDistinctRegions] = useState([]);
const [loading, setLoading] = useState(true);
// added state to track the selected region
const [selectedRegion, setSelectedRegion] = useState("");
useEffect(() => {
CountriesAPI().then(res => {
onLoad(res);
setLoading(false);
});
}, []);
const onLoad = dataList => {
setData(...data, dataList);
getRegions(dataList);
};
const getRegions = dataList => {
let regions = [];
dataList.map(dataItem =>
dataItem.region.length ? regions.push(dataItem.region) : ""
);
let regionsFiltered = regions.filter(
(item, index, arr) => arr.indexOf(item) === index
);
setDistinctRegions(...distinctRegions, regionsFiltered);
};
const renderLoading = () => {
return <div>Loading...</div>;
};
// now we only need to update the selected region
const change = event => {
setSelectedRegion(event.target.value);
};
const renderData = (dataList, distinctRegionsItem) => {
if (dataList && dataList.length) {
return (
<div>
<Container>
<Input type="text" placeholder="Search for a country..." />
<Select className="select-region" onChange={change}>
<option value="" hidden>
Filter by region
</option>
// added show all
<option value="">Show All</option>
{distinctRegionsItem.map(item => {
return (
<option key={item} value={item}>
{item}
</option>
);
})}
</Select>
</Container>
<CardList>
// filter the array based on selectedRegion and then render the list.
// if selectedRegion is empty show all
{dataList
.filter(
country => !selectedRegion || country.region === selectedRegion
)
.map(country => (
<CountryCard
population={country.population}
region={country.region}
capital={country.capital}
flag={country.flag}
key={country.alpha3Code}
id={country.alpha3Code}
name={country.name}
/>
))}
</CardList>
</div>
);
} else {
return <div>No items found</div>;
}
};
return loading ? renderLoading() : renderData(data, distinctRegions);
}
Upvotes: 1