Consider following pattern:
const Comp = ({ handler })=> {
// handler is some callback of the form ( ) => void
useSomeHook(handler);
//...
}
function useSomeHook(cb) {
const ref = useRef()
useEffect(() => {
ref.current = cb; // update ref before second useEffect
})
useEffect(() => {
// use the current (most recent) ref value
const iv = setInterval(() => { ref.current() }, 3000)
return () => { clearInterval(iv) };
}, []) // run only after first render
}
Question: Can I rely on the fact, that first useEffect
is always executed before second useEffect
, so ref
has already been updated?
Background: My intent is to avoid memoizing passed-in callback in Comp
(useCallback
) and setting cb
as dep. I am specifically interested, if this mutable ref pattern is valid in React world - above is just a contrived example.
I thought, the answer would be a clear "Yes!", after having read a tweet from Dan Abramov. Though, following issue states:
We do not make guarantees about sibling order, whether within a component or between siblings. This is because strong guarantees there hinder refactoring. Within a component, you should be able to re-order Hooks—especially, custom ones.
Did I have interpreted above statement wrong - or are the links a bit contradictory? Appreciate any clarifications.
PS: I am aware of linked post. This question is more about the general pattern, official docs and related to mentioned contradictory(?) statements.
Thanks!
If we have multiple instances of useEffect in the component, all the useEffect functions will be executed in the same order as they are defined inside the component.
Documentation mentions that by using this hook, we tell react that your component needs to do something after the it is done rendering. IMPORTANT: After the component is done rendering, it checks each useEffect hook sequentially in order which they are written.
Child Effects Fire First So the useEffect Hook behaves similarly to the class lifecycle methods. One behaviour to note is that the child callback is fired prior to the parent callback.
By default, useEffect will run on initial render as well as every future render (update) of your component.
You can’t use useEffect (or any other hook) in a class component. Hooks are only available in functional components. If you want to refactor your lifecycle methods to use useEffect, you have to refactor entire class components writ large. This is both time-consuming and prone to error. What if you could refactor just this one part of the code?
How to Use useEffect (and other hooks) in Class Components 1 Using useState. 2 Thinking in Effects. However, working with useEffect is a bit different. It’s designed to replace several lifecycle... 3 Use Hooks With Adapter Components. We can see a solution: create a functional component that encapsulates the useEffect... More ...
The order you call them in matters, as far as I'm aware, two useEffect calls in a component will always run in the same order. That's how React identifies which hook is which (based on order).
Using useEffect () in React.js functional component. The React hook useEffect helps in adding componentDidUpdate and componentDidMount combined lifecycle in React’s functional component. So far we know we can add lifecycle methods in stateful component only. To use it, we will need to import it from react −.
The order you call them in matters, as far as I'm aware, two useEffect calls in a component will always run in the same order. That's how React identifies which hook is which (based on order).
One of the main things you can't do is conditionally run a hook for the reason mentioned above: React keeps track of hooks that are called based on the order (index) they are called in the component.
This quote, is talking about the order of cleanup calls in components and between parent and child components. Typically, what you do in the clean up part shouldn't be something that would affect other hooks. Usually what you do is clean up any lasting side effect in your hook. Things like cancelling API calls and clearing intervals.
We guarantee that parent effects are destroyed before the child ones. The reason for this is that parents often tend to depend on some resource created by the child. Such as removing a listener from a DOM node managed imperatively by a child. If the child disposes its resources first, the parent might not be able to properly clean itself up. This is not specific to Hooks — it’s how componentWillUnmount works as well.
We do not make guarantees about sibling order, whether within a component or between siblings. This is because strong guarantees there hinder refactoring. Within a component, you should be able to re-order Hooks—especially, custom ones. Between siblings, it is expected that you can re-order them safely too. It is also common that only one sibling updates or unmounts, so dependencies between siblings cannot be reliable anyway.
The order between parent and child are set. For example, a Parent component that renders 2 Children.
<Parent>
<Child/>
<Child/>
</Parent>
Edit: This is how I'd refactor your code:
I'd make sure to include the cb in the initial useRef, and then use the cb in the dependency array of the first effect to update the ref.
Then you'd make sure to clear the interval in the second effect.
const Comp = ({ handler }) => {
// handler is some callback of the form ( ) => void
useSomeHook(handler);
//...
};
function useSomeHook(cb) {
const ref = useRef(cb);
useEffect(() => {
ref.current = cb; // update ref before second useEffect
},[cb]);
useEffect(() => {
const id = setInterval(() => {
ref.current();
}, 3000); // use the current (most recent) ref value
return () => {
clearInterval(id);
};
}, []); // run only after first render
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With