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.

Bug: useLayoutEffect callback called twice when a dom node is in a dependency array

See original GitHub issue

React version: v16.3.1

Steps To Reproduce

  1. add useLayoutEffect
  2. add dom node to the deps array
  3. invoke setState inside useLayoutEffect callback

Link to code example: https://codepen.io/everdimension/pen/rNOpZGK

The current behavior

useLayoutEffect callback gets called a second time, although the values in the deps array have not changed

The expected behavior

useLayoutEffect callback should not get called unless values in the dependency array change

Issue Analytics

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

github_iconTop GitHub Comments

1reaction
vkurchatkincommented, May 6, 2020

@bvaughn

If you have to clean up when your DOM node changes, you have two options. First is to use effects, which means that you HAVE to put the node in state:

React.useLayoutEffect(() => {
  if (!domNode) {
     return;
  } 
  const resizeObserver = new ResizeObserver(...);
  ...
  return () => {
     resizeObserver.disconnect();
  }
}, [domNode])

The second is to do it manually:

  const cleanupRef = React.useRef();
  const ref = React.useCallback(domNode => {
    if (cleanupRef.current) {
      cleanupRef.current();
    }

    cleanupRef.current = undefined;

   if (!domNode) {
     return;
   }
     const resizeObserver = new ResizeObserver(...);

     cleanupRef.current =  () => {
        resizeObserver.disconnect();
     };
  }, []);

React.useEffect(() => {
  return () => {
     if (cleanupRef.current) {
      cleanupRef.current();
    }
  };
}, [])

While you can certainly use the second option, and even wrap it in some sort of hook, I would rather just go with the first one.

0reactions
vkurchatkincommented, May 7, 2020

The idea is to create a reusable hook which doesn’t create a ref object, but accepts a ref

There is no feasible way to detect when such a ref changes. But IMO this is fine. When a hook accepts a DOM ref it is a clear signal to the user, that this ref should not change until the component is unmounted and they should structure their code accordingly.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React Hooks: useEffect() is called twice even if an empty array ...
It is the feature of ReactJS while we use React.StrictMode. StrictMode activates additional checks and warnings for its descendants nodes.
Read more >
React 18 - Avoiding Use Effect Getting Called Twice
For React Hooks in React 18, this means a useEffect() with zero dependencies will be executed twice. Here is a custom hook that...
Read more >
Hooks API Reference - React
Returns a memoized callback. Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that...
Read more >
Avoiding useEffect with callback refs - TkDodo's blog
Interacting with DOM nodes doesn't necessarily need useEffect. ... The empty dependency array is okay because the only thing used inside is ...
Read more >
React Hooks Common Mistakes - Bugfender
import React, { useEffect, useState, useCallback } from "react"; import ". ... Either include it or remove the dependency array.
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