I've made a React component that acts as a popup menu for a table (an ag-grid to be exact), meaning this component is rendered every time a table cell is opened and then unmounted/deleted every time the cell is closed.
This component I've made calls FetchAPI within it's useffect, so that once data needed for the component is recieved from a GET, it re-renders the component with the data
Code)
const myPopup = forwardRef((props, ref) => {
const [componentData, setComponentData] = useState({});
useEffect(() => {
fetch(`myurl.com/getdata/1`) //fetch from URL
.then((response) => response.json())
.then((data) => setComponentData(data)); //set component data hook with response (should trigger re-render with response data)
}, []);
console.log(componentData);
. . .
});
When I open a table cell for the first time, the component displays exactly as expected, and console shows two things:
{} //from the initial state/render of component
{1:'res', 2:'imm',...} //the data loaded (from the re-render after componentData is set to the FetchAPI result)
When I open a table cell for the second time, the same thing: it works as expected
When I open a cell the third time, my component renders with {} (the initial state) and the re-render never occurs with the data from FetchAPI. Looking at console, all I have is:
{} //re-render never occured, meaning FetchAPI never called setComponentData() with it's response
I looked deeper into it and it appears that FetchAPI does not make a call at all on this third time, as when I take a look at our server, there's no incoming requests for it after the first two (which would explain why the component never re-renders; FetchAPI doesn't get a response and thus never gets to the .then() to call setComponentData() )
With that, why is FetchAPI not making the call after the second one? It boggles me that this works the first two times but not after.
Any help would be grealty appreciated, this bug has been driving me nuts for a few weeks now
Let's start w the problem: A ref should be used to pass a component or pass a reference to a variable as to not re-render the component accessing that variable. They should be used inside of a useEffect() hook, not the other way around. If you want to pass a function (such as a fetch function), pass it down as a prop. Then, put the function in the useEffect hook of the child component.
Now, to answer your question: The useEffect hook is called when the component renders. A reference to the component won't trigger a render. Therefore, it will only trigger the useEffect on the initial render.
Do this instead:
const ParentComponent = () => {
const myFetchFunction = async url => {
fetch(url) //fetch from URL
.then((response) => response.json())
return(
...
<myPopup initFetchFunction={myFetchFunction}/>
...
);
//Or however you want to implement your component.
}
const myPopup = props => {
const [componentData, setComponentData] = useState({});
useEffect(() => {
props.initFetchFunction(`myurl.com/getdata/1`).then((data) => setComponentData(data));
}, []);
useEffect(()=> {
console.log(componentData);
},[componentData];
. . .
};