Unknown developer
Unknown developer

Reputation: 5950

React component does re-render, however effect does not run whereas it should

This is my Component:

function Test() {
    const [data, setData]=useState<Array<string>>([]);
    console.log('Hello:', data);
    
    useEffect(()=>{
       console.log('data: ', data)
    }, [data])
    const test1 = () => {
        setData(data.concat('a'));
    }
    const test2 = () => {
        setData(data);
    }
    return (
        <>
           <button onClick={test1}>Button one</button>
           <button onClick={test2}>Button two</button>
       </>
    );
}

Everything works fine when clicking Button one. Component does re-render and effect does run. However, what happens with Button two is something I cannot explain:

Could someone explain the above behaviour?

Upvotes: 1

Views: 92

Answers (2)

Saif Hamdan
Saif Hamdan

Reputation: 19

what happened is when you use setData(data); the Component Re-renders no matter if the Data has actually changed or not, but when it comes to useEffect the way it compares values it takes the old values and compares it with the new ones if the values have changed it will execute what inside it otherwise it won't, this behavior will also happen if the data is an object it will check each value inside it to decide whether the object has changed or not

Upvotes: 0

RubenSmn
RubenSmn

Reputation: 4672

If the new state is the same as the current state, as you mentioned by Object.is React will skip the re-render but as mentioned in the React useState docs React might still call the component.

Although in some cases React may still need to call your component before skipping the children, it shouldn’t affect your code.

This results in running the console.log with the "Hello: ", data values.

And so, React does not actually re-render the component.

We can see this with a useEffect with no dependency array which acccording to the React useEffect docs should run every re-render.

Effect will re-run after every re-render of the component.

useEffect(() => {
  console.warn("Render");
});

As you can see this is not the case.

const {useState, useEffect, Fragment} = React;

function Test() {
  const [data, setData] = useState([]);
  console.log("Hello:", data);

  useEffect(() => {
    console.warn("Render");
  });

  useEffect(() => {
    console.log("data: ", data);
  }, [data]);
  const test1 = () => {
    setData(data.concat("a"));
  };
  const test2 = () => {
    setData(data);
  };
  return (
    <Fragment>
      <button onClick={test1}>Button one</button>
      <button onClick={test2}>Button two</button>
    </Fragment>
  );
}

ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <Test />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

Upvotes: 1

Related Questions