An open API service indexing awesome lists of open source software.

https://github.com/icyjoseph/how-to-usecallback

Understanding the correct usage and benefits of useCallback
https://github.com/icyjoseph/how-to-usecallback

hooks react react-hooks reactjs usecallback

Last synced: about 2 months ago
JSON representation

Understanding the correct usage and benefits of useCallback

Awesome Lists containing this project

README

          

# useMemo - useCallback

In this repository, the `useCallback` hook is used to prevent unnecessary rendering of a children component.

The hook, `useMemo` is also used to show how React help us prevent unnecessary calculations inside a component.

### Counter

```jsx
export function Counter({ inc, dec, count }) {
return (
<>


{count}



INC
DEC
>
);
}
```

This function component simply takes a count and shows it. It also give the user two buttons to increase or decrease the count. Mind that this component does not hold a count state itself!

### Expensive

```jsx
export function Expensive({ total, totalCb }) {
const handleClick = () => {
totalCb();
};

console.log("Expensive re-renders, with: ", total);

return (
<>


total: {total}




Calc Total

>
);
}

export default React.memo(Expensive);
```

This so called Expensive component receives two props, `total` and `totalCb`. The latter is called when the user clicks the calculate total button.

Notice that we export Expensive wrapped by `React.memo`.

### App

```jsx
function App() {
const [time, tickTime] = useState(0);
const [count, setCount] = useState(0);
const [total, setTotal] = useState(0);

// "ticks" to re-render the whole App
useEffect(() => {
const timer = setInterval(() => tickTime(time + 1), 1000);
return () => clearInterval(timer);
});

// setters
const inc = () => setCount(count + 1);
const dec = () => setCount(count - 1);

// memoize an array
const arrMemo = useMemo(() => {
console.log("Create a new array only when count changes:", count);
return makeArr(count);
}, [count]);

// same instance callback
const totalCb = useCallback(() => {
console.log("inside callback totalCb", arrMemo);
setTotal(totalCalc(arrMemo));
}, [arrMemo]);

// every tick triggers a new instance
const newEveryTime = () => {
console.log(
"This callback is dirty, and will force Expensive to re-render"
);
};

return (


Hello CodeSandbox {time}


Start editing to see some magic happen!





);
}
```

This component has a very special behavior. It increases a timer by one every 1000 ms and displays that update to the DOM.

When this update happens, Counter and Expensive render again. However, Expensive is memoized as a component and furthermore, it takes fully memoized props, and therefore it does not perform an unnecessary render.

That's because:

- we call useCallback to generate `totalCb`.
- `total` does not change unless the user increases it or decreases it inside Counter.

You notice, there's a `newInTheBeginning` prop. This is pointing to a function defined for the whole module, so it is the same function every tick of the timer.

Unfortunately, if we remove the comment from the last two props, `willBeNewEverytime` and `newEveryTime`, then each tick, will make a new reference to these and hence trigger a render run, in spite of `React.memo` wrapping Expensive.

That's how useCallback helps us, it saves the reference to a function for further rendering operations. Since the reference is the same, children which consume it do not render again.

A new reference is made only if the dependencies to the callback change. In this case, that would be `arrMemo`. Which in turn, changes when `count` is modified.

Additionally we create a memoized value, by calling `useMemo` we can define a value derived from the `count` state, if we didn't `useMemo`, every tick would trigger a derivation!