This is my first question, so please, bear with me. I’m working on a SPA React project which tracks farms and their associated beds and could use some help. I’d like to render some data on a FarmDetail page by filtering over initial state with useParams(), rather than making more fetch requests to farms/:id. I’ve made 1 fetch get request and saved initial state in an allFarms variable. I’ve rendered some Links using react-router-dom which navigate to a path using the id of the clicked farm.
So far, the component renders twice, and on the second pass my console.log(farm) in useEffect does output the correct farm object. Trouble is, at that point it doesn’t rerender as I’m not setting state and the component has already rendered so the return value is blank since farm = {}.
import { useParams } from "react-router-dom";
function FarmDetail({ allFarms, setAllFarms }) {
console.log("render");
let { id } = useParams();
id = parseInt(id);
let farm = {};
useEffect(() => {
if (allFarms) {
farm = [...allFarms].find((f) => f.id === id);
}
console.log(farm);
}, [allFarms]);
console.log(farm);
console.log(allFarms);
return (
<div>
<p>{farm.name}</p>
</div>
);
}
export default FarmDetail;
I’ve tried to useState for the farm variable and setState in useEffect in order to trigger a rerender, but that doesn’t seem to work as farms is then set to undefined and I never trigger the condition in useEffect. allFarms also just logs an empty array multiple times which I don’t quite understand why. I end up with a TypeError: cannot read properties of undefined (reading "name") which makes sense to me since farm remains undefined that way. I feel like I’m close, but I’m missing something here. Any help is much appreciated!
2
Answers
There’s no need for an effect hook here because there are no side-effects.
What you can do instead is memo-ise the
id
/allFarms
combinationThis will re-calculate
farm
any timeid
orallFarms
changes and re-render the component.Even this may not be necessary if changes to
allFarms
causes theFarmDetail
component to re-render. If so, you can even more simply use the followingGiven that this may return
undefined
ifallFarms
is not an array or theid
not present, you should use some conditional renderingI saw your code.
And I am agree with Phil’s answer.
You can use useMemo hook instead of useEffect.
And by the way, the reason, you component renders twice, is that
first your component renders (with initial allFarms data)
sencod your component renders (with fetched allFarms data)
And the reason, your component renders blank, is that you mentioned that
inside your component.
So every re-render, farm will initialized as blank {}
but
only called when allFarm variable changed.
Hope this answer helps.