question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

useEffect call recursively when add functional props as dependency

See original GitHub issue

What is the current behavior?

  1. Define a component with useEffect and useState
const [style, setStyle] = useState({ color: "#000000" });
const [signature, setSignature] = useState("Your Name");

useEffect(() => {
    html2canvas(document.getElementById("signature-container")).then(
     canvasEl => {
       onChange(canvasEl.toDataURL());
      },
   );
}, [signature, style]);
  1. Pass onChange function as props <Text onChange={handleChange} />

It’s warn need to add onChange prop into dependency array of useEffect but If I add onChange prop into the array, useEffect call recursively

What is the expected behavior? Don’t need to warn if user don’t pass function props as dependency

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

20reactions
gaearoncommented, Apr 2, 2019

OK this is interesting. Normally you’d fix this by passing an onChange with a stable identity:

const handleChange = useCallback(() => {
  /* do stuff */
}, [/* its deps */])

<Text onChange={handleChange} />

But in your case it wouldn’t work because it’s inside Formik’s render prop callback.

I’m not sure what’s the idiomatic solution here from Formik’s point of view.

But in your case, it seems like it’s not actually important to re-run onChange if the onChange function itself changes. Therefore, you can use a ref to explicitly track its latest value:

  const latestOnChange = useRef(onChange);
  useEffect(() => {
    latestOnChange.current = onChange;
  }, [onChange]);

And then you can all latestOnChange.current in the effect.

https://codesandbox.io/s/l5z8z3jj3z

4reactions
enrique-ramirezcommented, Jan 11, 2022

Was there ever a proper answer to @gaurav5430 's question? I’m wondering the same. It feels odd to ask parents to pass functions with stable identity’s as props. There’s no way to know WHEN you need to do this, and asking them to use useCallback everywhere don’t seem like a good idea.

I, too, have a shared component library and it seems to me unfeasible to ask consumers to always pass useCallback or else have the component break. I’ve relied on the useRef hack for a while, but it comes with its own limitations. Sometimes I also just ignore the empty dependency list and be done with it.

Quick sample would be a modal component:

const Modal = ({ onClose }) => {
  useEffect(() => {
    const closeOnEscape = (event) => event.key === 'Escape' ? onClose() : null

    document.body.addEventListener('keydown', closeOnEscape)
    return () => document.body.removeEventListener('keydown', closeOnEscape)
  }, [])

  return createPortal(...)
}

Is there a best practice for this, @gaearon ?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Recursive/cyclic call to useEffect - React - Stack Overflow
I have a component that triggers the useEffect if it receives an update from the parent element. So it calls a function that...
Read more >
Understanding Dependencies in useEffect | Bits and Pieces
A deep dive into the dependencies array in React.useEffect(). What causes stale values from previous renders in React.
Read more >
Avoiding recursive useEffect hooks in React - Jack Franklin
Therefore we can lose it from our useEffect dependencies array, meaning that useEffect won't rerun when it changes!
Read more >
How the useEffect Hook Works (with Examples) - Dave Ceddia
Run useEffect When a Prop Changes​​ useEffect can trigger on any of them. In this example, the PropChangeWatch component is receiving 2 props...
Read more >
useMemo - React Docs
React will call your function during the initial render. On subsequent renders, React will return the same value again if the dependencies have...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found