Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useEffect array dependency is called in every render when array is not changed

Tags:

reactjs

import React, { useState, useEffect } from "react";

export default function App() {
  const [columns, setColumns] = useState([
    { name: "a" },
    { name: "b" },
    { name: "c" }
  ]);
  const [isOpen, setIsOpen] = useState(false);

  const addName = () => setColumns([...columns, { name: "r" }]);
  const toggleOpen = () => setIsOpen(!isOpen);

  return (
    <>
      <List columns={columns} />
      <button onClick={toggleOpen}>Toggle</button>
      <button onClick={addName}>Add</button>
      <p>{isOpen.toString()}</p>
    </>
  );
}

const List = ({ columns }) => {
  const names = columns.map(col => col.name);
  useEffect(() => {
    console.log("Names is changed to: ", names);
  }, [names]);

  return <p>{names.join(" ")}</p>;
};

Names is changed to: is called, when isOpen state is changed in App component. I want the console.log to be executed only when names array is changed. I think in List component, it is creating a new array whenever render, so that the previous array and the new array are not equal.

like image 503
Henok Tesfaye Avatar asked May 16 '20 15:05

Henok Tesfaye


People also ask

Is useEffect called on every render?

Does useEffect run after every render? Yes! By default, it runs both after the first render and after every update.

What happens if we don't pass dependency array in useEffect?

So what happens when the dependency array is empty? It simply means that the hook will only trigger once when the component is first rendered. So for example, for useEffect it means the callback will run once at the beginning of the lifecycle of the component and never again.

How does useEffect dependency array work?

The useEffect hook allows you to perform side effects in a functional component. There is a dependency array to control when the effect should run. It runs when the component is mounted and when it is re-rendered while a dependency of the useEffect has changed.

Does useEffect with dependency array run on first render?

Side Effect Runs After Every Render The first is the default case. If you do not pass the dependency array to the useEffect hook, the callback function executes on every render. Thus React will run the side effect defined in it after every render.


1 Answers

You should memoize the component so it will render only on props change (or if comparison function passed as 2nd argument).

Currently, List rendered due to its parent App render.

const List = ({ columns }) => {
  const names = columns.map((col) => col.name);
  useEffect(() => {
    console.log("Names is changed to: ", names);
  }, [names]);

  return <p>{names.join(" ")}</p>;
};

const MemoList = React.memo(List);

export default function App() {
  return (
    <>
      <MemoList columns={columns} />
    </>
  );
}

See working example:

Edit polished-pine-j17om


For class component, use React.PureComponent or implement shouldComponentUpdate.

like image 90
Dennis Vash Avatar answered Oct 13 '22 12:10

Dennis Vash