Table of contents
- What is React useCallback
- useCallBack Reference
- Examples and Use-cases
- Caching the Values or Function
- When to use useCallback vs useMemo?
- Difference of useMemo with useCallback
- When to use useCallback
- The difference between useCallback and declaring a function directly
- Updating state from a memoized callback
- Preventing an Effect from firing too often
- Optimizing a Custom Hook
- Conclusion
This is an easy to read guide, where we will cover
What is React
useCallback
?When to use React
useCallback
? (use-cases)Real world examples of
useCallback
TroubleShooting Tips
What is React useCallback
useCallback is a React Hook. (Hooks are JS functions that let's you add additional pre-built functionality to your component)
useCallback
is a hook that lets you cache a function definition between renders.
useCallback
does not call the function and cache the return value. It caches the function definition itself.
useCallBack Reference
useCallback(function, [dependencies]);
As useCallback
is a react Hook you need to call it at the top level of your component
import { useCallback } from 'react';
export default function ReactCommentsSection({ postId, username }) {
const handleCommentSubmission = useCallback((Text) => {
post('/post/' + postId + '/comment', {
username,
comment: commentText,
});
}, [postId, username]);
}
Parameters
fn : (function to memorize)
This is the function that you want to cache.
This function can take any arguments and can return any data
The React will return this function definition (but not call it) during the first render.
On the next renders:
If the
dependencies
are not changed then the react will give you the same cached functionElse if, dependencies have changed the react will give you the current function that you have passed it. That is if the dependencies have changed.
Dependencies
An array of reactive values that the function depends on or are referenced by the fn
Reactive values are props, state and all the variables/ functions that are inside your component
The react will do a shallow check to with the Object.is
method to check if the dependencies have changed
You can use code linter to verify if all the dependencies are listed in the dependencies array
The list of dependencies should remain constant and will be written in line in an array like [dep1, dep2, dep3]
What does use callback Return
initial Render: On initial render the
useCallback
hook returns the function that you have passed itSubsequent Renders :
If the dependencies have not changed, it will return the cached function
If the dependencies have changed, it will return the new function, that is the function you have passed during this render
Caveat
Always declare useCallback
at the top of your component because it is a hook
useCallback
should never be used inside loops or conditionals. If you need to use it inside loops or conditions, extract the code into a new component
Cache Management
React will not throw away a cached function unless there is a very good reason to do that
like: during development react will through a cached function, if you edit the component
If a component suspends the during initial mounting phase then the react will discard the cached
Examples and Use-cases
Use-Case : Skipping Re- rendering of components for performance optimization
1. un-possessory re-renders
When developing large-scale applications or applications with complex logic, it becomes necessary to optimize performance.
Because of resource limitations, the logic or the scale of component and prop drilling can cause the UI to not perform optimally
Caching the Values or Function
One of the methods of optimizing performance is caching the values that require a lot of computation and that don't change much from one re-render to another
you can use the useMemo
for caching the values, but what about functions?
As {}
notation creates a new Object, the function notation like function () {}
or () => {}
creates a new function
Normally this isn't an issue but creating new function on every re render defeats the purpose of caching
So, we need to cache the functions that we are passing down as props here useCallback
comes in handy
Example 1 : The Comment section
Consider a comment section on a website. Here we have CommentSection
Component
import { useCallback } from 'react';
export default function CommentSection({ userName, userToken, postId, }) {
const handleCommentSubmission = useCallback((textOfTheComment) => {
post('/post/' + postId + '/comment', {
username,
comment: textOfTheComment,
token: userToken,
});
}, [postId, userName, userToken]);
}
Here we have a CommentSection
Component. Our component takes three props : 1. userName
, userToken
, and postId
Then we have the handleCommentSection
function that has the useCallback
hook to which checks if any of the dependencies in the dependencies array has changed
the dependencies are : [postId, userName, userToken]
If the dependencies have not changed then the useCallback
returns the cached function otherwise the useCallback
returns the new function
When to use useCallback vs useMemo?
Both useCallback and useMemo are used to cache something to optimize performance
useMemo is used to cache values and useCallback is used to cache function definitions
So, as to prevent unnecessary re-renders
Difference of useMemo with useCallback
The useMemo
hook takes a function and a dependency array as arguments
The useMemo
calls the function and caches its returned value, the function is then only called when any of the dependencies change
const ComponentCompute = useMemo(() => {
return calculateReq(product);
}, [product]);
here the returned value of calculateReq
will be cached and this will only be re-calculated when the product
changes
useCallback
also takes a function and a dependency array, but instead of caching the return value of the function it caches the function declaration itself
useMemo
const requirements = useMemo(() => { // Calls your function and caches its result
return computeRequirements(product); }, [product]);
useCallback
import { useCallback } from 'react';
export default function CommentSection({ userName, userToken, postId, }) {
const handleCommentSubmission = useCallback((textOfTheComment) => {
post('/post/' + postId + '/comment', {
username,
comment: textOfTheComment,
token: userToken,
});
}, [postId, userName, userToken]);
}
When the handleCommentSubmission
function gets called, useMemo calls the cached version and that remains the same across renders except when one of the dependencies change
If you are familiar with useMemo then you can think of useCallback as a wrapper around useMemo with built in functionality
you can use useMemo instead of useCallback by adding a callback function like so
function useCallback(fn, dependencies) {
return useMemo(() => fn, dependencies);
}
When to use useCallback
It depends on what type of app or website you have. If you have an app that is more like a website, that is you are changing Big parts or your UI at once then memorization of caching is not that useful
And if you only need to change small parts of your overall UI like in a drawing application or some other app
Where you load a whole lot of UI at once and then need to make small changes to the UI then caching would be a good strategy
Here are a few cases where useCallback is a good idea'
If you are using
useMemo
in a component and you want to pass a function to that component as prop.If you are using the function inside other React Hooks. For
example
another function wrapped inuseCallback
depends on it you depend on this function fromuseEffect
Note: The useCallback
does not prevent from creation a function. the function is always created and that is all good, React just ignores the created function and provides you with the cached version instead
Making Memorization and caching unnecessary by following a few principles:
- Wrap a component visually
When a component is visually wrapping other components like a big web page component containing smaller UI components like buttons etc
Let it accept JSX as its children. like so,
<div>
<img />
<button />
</div>
Then if the wrapper component updates react know not to update the child components.
2. Local State
Always prefer local state and do not lift the state. Only lift the state when necessarily required.
So this importantly with short lived data like when a user is hovering over a button or there is a small form
3. Keeping the rendering logic pure
The component should always be pure. Meaning for a given input props the output should always remain the same
or re-rendering the component should never give different outputs
Fix the bug instead of caching the component
4. Avoid unnecessary effects that update state
Effects are updates that the application asks react to perform after the UI update is over. However if there are lots of updates then it makes the app run in circles and causes performance issues
hence, only use updates when necessary
5. Removing unnecessary dependencies from your effects
instead of caching the values and the function declaration, you can try to reduce the dependencies like move out some objects or function
IF the interaction is still laggy use the React dev tools profiler to find out which components can benefit from memorization
The difference between useCallback and declaring a function directly
- Declaring a function directly
Directly declaring a function inside a component, creates a new instance of the function every time the component re renders. This is because as {}
creates a new Object a new functional declaration like function (){}
or ()=>{}
creates a new function
function SomeComponent(props) {
function handleClick() {
console.log('Somebody clicked the button');
}
return <button onClick={handleClick}>Click the button </button>;
}
Here everytime the SomeComponent is rendered on screeen a new handleClick
button is created.
2. useCallback
the useCallback
hook memorizes the function defination itself. Now, when the SomeComponent
is rendered on the screen the useCallback gives you the cached version of the handleClick
method
import { useCallback } from 'react';
function SomeComponent(props) {
const handleClick = useCallback(() => {
console.log('user clicked button');
}, []); // function get recreated when dependencies change, here the array is empty meaning the function will never be recreated
return <button onClick={handleClick}>Click the Button!</button>;
}
Using the useCallback the handleClick method is only created once and cached by the react and the cached copy will always be used thus improving performance
This article is brought to you by DeadSimpleChat, Chat API and SDK for your website and apps.
Main Difference
Directly creating a function will create a new instance of the function every time the component re-renders
useCallback
will cache the function and reuse the function definition thus giving the cached copy every time
Updating state from a memoized callback
In React you can update the state of a component using the setState function.
There are two options when you need to update the state based on the previous state
Reading the current state from inside the callback
using an updater function
Reading the current state from inside the callback
const handleTodo = useCallback((todoText) => {
const newTodo = { id: nextId++, todoText };
setTodos([...todos, newTodo]);
}, [todos]);
Here
We are memorizing or caching the handleTodo function with
useCallback
the
todos
state variable is specified as a dependency because we are reading the state directlyHere every time the todos change a new version of handleTodo will be created
using an updater function without direct dependency on state
const handleAddTodo = useCallback((text) => {
const newTodo = { id: nextId++, text };
setTodos(todos => [...todos, newTodo]);
}, []);
Here instead of reading the
todos
inside of reading the todos directly and hence creating the nessissity of adding it as a dependency in the the dependencies array of useCallbackwe are providing an updater function. This updater
func
takes the previous todos as an argument and calculates the new todos stateThus the benefit here is that the handleTodo now no longer needs to update whenever todos changes thus using the cached version more and improving the performance
Preventing an Effect from firing too often
Many times you have use the useEffect
hook and you need to call a function inside the hook
This creates a problem, every value must be declared in the dependency array.
To optimize the useEffect
and component rendering for performance purposes you can wrap the function that you are calling from useEffect
into useCallback
.
Optimizing a Custom Hook
Hooks are functions designed to encapsulate reusable functionality. This is why you can also create custom hooks of your own, this promotes code reusability and maintainability
- Need for optimization in custom Hooks
There is a need for optimization in custom hooks that return functions.
This is because these hooks can become problematic for components that have the returned function as a dependency.
The re creation of these functions at every render of the component could cause performance issues
2. useCallback
for stable functions
You can use the useCallback
hook to cache these functions thus optimizing performance.
Conclusion
In this article, we learned about the useCallback
react hook with use-cases and examples
I hope you liked the article. Thank you for reading.