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 Link
s 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.
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
combination
const farm = useMemo(() => allFarms?.find((f) => f.id === id), [id, allFarms]);
This will re-calculate farm
any time id
or allFarms
changes and re-render the component.
Even this may not be necessary if changes to allFarms
causes the FarmDetail
component to re-render. If so, you can even more simply use the following
const farm = allFarms?.find((f) => f.id === id);
Given that this may return undefined
if allFarms
is not an array or the id
not present, you should use some conditional rendering
if (!farm) {
return <p className="alert warning">Not found</p>;
}