Wojciech Brożonowicz
Wojciech Brożonowicz
1 min read

Categories

Tags

React useCallback hook

UseCallback hook is similar to useMemo. Typical scenario to use this callback is when:

  • We pass in props callback function to child component, and this function is changing value of parent component
  • without it, child component will be also rerendered (ref of calback function is changed between rerenders)
  • with useCallback child component will not be rerender

OK, example:

import React, { useState } from 'react';

import ClickCounter from './ClickCounter';
import ClickCounter2 from './ClickCounter2';

const App = () => {

  const [counter1, setCounter1] = useState(0)
  const [counter2, setCounter2] = useState(0)

  const addClickToClickCounter1 = 
  () => setCounter1(prevValue => prevValue + 1 )

  const addClickToClickCounter2 = 
  () => setCounter2(prevValue => prevValue + 1 )

  return (
    <>
      <p>clicks 1= {counter1}</p>
      {<ClickCounter callback={addClickToClickCounter1} ></ClickCounter>}
      <p>clicks 1= {counter2}</p>
      {<ClickCounter2 callback={addClickToClickCounter2} ></ClickCounter2>}
    </>
  )

}

export default App;

And child components as below:

import React from 'react';

const ClickCounter = ({ callback }) => {
    console.log('i am rendered , on btn1' )
    return (
        <div>
            <button onClick={callback} >Add to1</button>
        </div>);
}

export default React.memo(ClickCounter);
import React from 'react';

const ClickCounter2 = ({ callback }) => {
    console.log('i am rendered , on btn2' )
    return (
        <div>
            <button onClick={callback} >Add to2</button>
        </div>);
}

export default React.memo(ClickCounter2);
  • now ClicCounter 1 and 2 are rerendered every time without need (they are only buttons…), even React.memo not helped this time. Reason: ref of function is different.

So let’s fix it with useCallback hook and React.memo:

import React, { useState, useCallback } from 'react';

import ClickCounter from './ClickCounter';
import ClickCounter2 from './ClickCounter2';

const App = () => {

  const [counter1, setCounter1] = useState(0)
  const [counter2, setCounter2] = useState(0)

  const addClickToClickCounter1 = 
  useCallback(() => setCounter1(prevValue => prevValue + 1 ),[])

  const addClickToClickCounter2 = 
  useCallback(() => setCounter2(prevValue => prevValue + 1 ),[])

  return (
    <>
      <p>clicks 1= {counter1}</p>
      {<ClickCounter callback={addClickToClickCounter1} ></ClickCounter>}
      <p>clicks 1= {counter2}</p>
      {<ClickCounter2 callback={addClickToClickCounter2} ></ClickCounter2>}
    </>
  )

}

export default App;

Now child components (buttons) will be not rerender.

That’s all!