7
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.

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, how about for class components?
React.PureComponent & shouldComponentUpdate
React.memo will not help if List will utilize other props and/or state
4
const names = columns.map(col => col.name);

Creates a new array every time and useEffect thinks that dependencies have changed.

To avoid that either pass names directly to useEffect:

  useEffect(() => {
    console.log("Names is changed to: ", names);
  }, names);

Or useMemo to get the same array object:

const names = useMemo(() => columns.map(
   col => col.name
), [columns]);

2 Comments

thanks, what is the difference between passing a dependency in an array or directly passing the dependency?
names is an array, so you pass it's content as dependency. If you pass [names] only the reference to the array is checked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.