Javascript – React set Hook from function call outside Component

javascriptreact-hooksreactjs

I'm testing the new React Hooks, and I've encountered a behavior I cannot fix (and comprehend neither). Basically, I have my functional component and inside it a function in which i set the hook. This function is passed to a Component rendered, and it's called from this last rendered Component using the props.
Too bad that the hooks of the parent does not update accordingly!

I know it may seems difficult to understand, but I've reproduced the error here https://codesandbox.io/s/vvwp33l7o5

As you can see, in App Component I've the onResize function that should update the counter hook. This function is passed to the ResizeObserverContainerHook Component, and from this latter is called when ResizeObseverContainerHook div is resized. As you can see, the width and the height variables in onResize function are correct, but the counter hook seems not to update! In fact, it stays equal to 1 forever.

I don't know, it seemes that I cannot update the hook from outside the Component ( and that's like the state in React Stateful Component, but at least I could passing a function like in this case, but it doesn't work :/ ).

Any idea how I can make that setCounter at like 12 of index.js works?

Best Answer

The other answer is a bit confused - what you're doing is fine. When it says don't call hooks from inside stuff, it means don't call the actual API's conditionally or from within loops, e.g. conditionally defining useEffect or useState.

Your problem stems from the empty array you pass to the effect in the Observer.js, because it is empty, the effect never refreshes so the closure is stale such that when the onResize function calls setState, the counter value will always be the initial value of zero.

You need to have that useEffect dependent on something so that when the component updates, it cleans up the previous effect and attaches a fresh version of the onResize function to the ResizeObserver.

I poked around with your sandbox a bit: https://codesandbox.io/s/x9z7k245lq?fontsize=14

It now passes down the counter state to the Observer component and the effect will run each time the counter changes. The thing is that I also added some refs to the index to track the height/width so the state doesn't always update or else it will infinitely loop. I guess you can think of that as shouldComponentUpdate.

It takes a while to think in hooks properly. If you want a good article to read about hooks and avoiding some pitfalls regarding empty array and stale closures, check this out: https://overreacted.io/making-setinterval-declarative-with-react-hooks/