Search code examples
reactjsusecallback

Using React.memo and useCallback to prevent functions from causing re-renders


I am following this tutorial demonstrating react's 'useCallback' hook along with React.memo to prevent a function being render unnecessarily. To prove the concept we use useRef to console the number of renders. This worked with the function alone but i added a function to randomize the button background color and I can't seem to no prevent the rendering of both functions.

    import React,{useState, useCallback, useRef} from 'react';
import './App.css';

const randomColor = () => `rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`

const Button = React.memo(({increment, bgColor}) => {
const count = useRef(0)
console.log(count.current++)
return(
    <button onClick={increment} style={{backgroundColor: bgColor}}>increment</button>
  )
})

const App = React.memo(() => {
  const [count, setCount] = useState(0)
  const [color, setColor] = useState(`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`)



  const increment = useCallback(() => {  
    setCount(previousCount => previousCount + 1)
    setColor(randomColor)
  },[setCount,setColor])

  return (
    <div className="App">
      <header className="App-header">
        <h2>{count}</h2>
        <Button increment={increment} bgColor={color}>increment</Button>
      </header>
    </div>
  );
})

export default App;

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

    import React,{useState, useCallback, useRef} from 'react';
    import './App.css';

    const randomColor = () => `rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`

    const Button = React.memo(({increment, bgColor}) => {
    const count = useRef(0)
    console.log(count.current++)
    return(
        <button onClick={increment} style={{backgroundColor: bgColor}}>increment</button>
      )
    })

    const App = React.memo(() => {
      const [count, setCount] = useState(0)
      const [color, setColor] = useState(`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255}`)



      const increment = useCallback(() => {  
        setCount(previousCount => previousCount + 1)
        setColor(randomColor)
      },[setCount,setColor])

      return (
        <div className="App">
          <header className="App-header">
            <h2>{count}</h2>
            <Button increment={increment} bgColor={color}>increment</Button>
          </header>
        </div>
      );
    })

    export default App;

Solution

  • It the example in the video you mentioned, the Button component does not change, because the props stay the same all the time. In your example, the increment stays the same, but the problem is that the bgColor changes with each click.

    That means, that if you rendered only the main component and not the Button component, the background would have to be the same, but because it receives different background color each time, it would not make sense.

    React will always re-render the component if the props change (if you don't implement custom shouldUpdate lifecycle method).