Peter89
Peter89

Reputation: 706

react functional component re-render one time even the state's value not change

I'm a new fan of the React Functional Component. When I read the document and try to do some experiments then I figure out a doubting case.

I have a custom-hook "useAutoIncrement" that has a setInterval which increases the "num" every second. The main component re-render when this interval fires. Then I change setState a fixed number for custom-hook 0. As Expected, the main component wouldn't re-render. We have been good so far :)

Then try to click "button" increment of the event handler in the main component, the main one re-render, and the custom-hook re-render also. But when the interval runs in the first next time to setState value 0, the strange thing happens. The main component and the custom-hook seem to re-render (I place some consolg.log and it shows up), but there is no console.log show up inside the useEffect both main-component and custom-hook.

enter image description here

Take a look at this image, 3 blue-lines. The 1st blue-line is inside the hook-effect interval function. It calls setNum with un-changed value. The main component shouldn't re-render but it seems did. The 2nd and 3rd blue-line print out they re-render

Can anyone explain this case to me? Here is my code: https://codesandbox.io/s/react-playground-ddqzc?file=/index.js

import React, { useState, useEffect } from 'react';
 
const useAutoIncrement = () => {
  const [num, setNum] = useState(0)

  useEffect(() => {
    console.log("useAutoIncrement useEffect")
    const interval = setInterval(() => {
      console.log("useAutoIncrement useEffect interval")
      setNum((currentNum) => {
        return 0 // return fixed number
      })
    }, 5000)

    return () => {
      console.log("useAutoIncrement useEffect return")
      clearInterval(interval)
    }
  });

  console.log("render useAutoIncrement")
  return 0
}

const CustomHook = () => {
  const initialCount = {value: 5};
  const [count, setCount] = useState(initialCount);

  const num = useAutoIncrement()
 
  const handleIncrement = () => {
    setCount((currentCount) => {
      return Object.assign({}, currentCount, {value: 2})
    });

    console.log("handleIncrement")
  }


  useEffect(() => {
    console.log("CustomHook useEffect")
    
    return () => {
      console.log("CustomHook useEffect return")
    }
  })

  console.log("CustomHook render")
 
  return (
    <div>
      <h1>{count.value}</h1>
 
      <button type="button" onClick={handleIncrement}>
        Increment
      </button>

      <p>Auto Increase Number: {num}</p>
    </div>
  );
};
 
export default CustomHook;

Upvotes: 0

Views: 1441

Answers (1)

Deryck
Deryck

Reputation: 7658

When you clicked the button and fired the state change, your component was updated and re-rendered - you can see this from your logging above.

  • render useAutoIncrement
    • this is the first console message your component runs into when the function fires
  • CustomHook render
    • this is the next call in the JS stack
  • useAutoIncrement useEffect return
    • since you are re-rendering, the previous one is being dismounted, causing this to run but react does this in a particular order so you are seeing it just before the newly called useAutoIncrement's useEffect will be ran
  • CustomHook useEffect return
    • same thing happens here in the next useEffect
  • useAutoIncrement useEffect
    • react now runs the useEffect now that the previous one is cleaned up
  • CustomHook useEffect
    • and this one too
  • useAutoIncrement useEffect interval
    • interval occurs on time
  • render useAutoIncrement
    • as discussed, setState forces update
  • CustomHook render
    • since useAutoIncrement ran again, num is updated too, even tho it's the same number

Hope this helps some

Upvotes: 1

Related Questions