How to use useCallback in React

Muhammad Ata
4 min readApr 13, 2021

React hooks are the most powerful feature introduced in React 16.8 which allows you to manage states and other React features without writing classes. In this article, I am going to explain you about the usage of useCallback hook with practical implementation.

usCallback hook is used to avoid unnecessary re-rendering in your code component which makes your application more faster and efficient.

First of all, let's go through the sample Todo app component and understand the problem and then we will move forward to see how useCallback resolve re-rendering problem.

const App = () => {const [text, setText] = React.useState(‘’);const [todos, setTodos] = React.useState([
{id: ‘todo-1’,name: ‘First Todo’},
{id: ‘todo-2’,name: ‘Second Todo’}
])
const handleChangeText = (event: any) => {setText(event.target.value);}const handleAddTodo = () => {setTodos(todos.concat({ id: ‘todo-’+ todos.length + 1, name: text }));setText(‘’);}const handleRemoveTodo = (id: string) => setTodos(todos.filter(todo => todo.id !== id)), [todos]return ( <div> <input type=”text” value={text} onChange={handleChangeText} /> <button type=”button” onClick={handleAddTodo}> Add Todo </button> <TodoList list={todos} onRemove={handleRemoveTodo} /> </div>)}const TodoList = (props: any) => {console.log(‘Todo List is Rendering’);const { list, onRemove } = props;return (<ul>{list.map((todo: any) => (<ListItem item={todo} key={todo.id} onRemove={onRemove} />) )}</ul>)}const ListItem = ({ item, onRemove } : any) => {console.log(‘Todo Item is Rendering’);return (<li>{item.name}<button type=”button” onClick={() => onRemove(item.id)}>Remove</button></li>);}

The above code looks fine but the problem is when the text is changed in the App component TodoList and ListItem will be re-rendered which is unnecessary. Now let's test it by console.log in each of the above components

const App = () => {console.log(‘App is Rendering’);const [text, setText] = React.useState(‘’);………..}const TodoList = (props: any) => {console.log('Todo List is Rendering');.... }const ListItem = ({ item, onRemove } : any) => {console.log('Todo Item is Rendering');
....
}

When the user type anything from the App component then we will get the console from TodoList and ListItem as well.

Since state change is applied in parent (App) component, there should not be re-rendering perform in the child components.In order to prevent this behavior, we will use React memo API which will be responsible to prevent re-rendering the child components.

React memo basically memoized your component's function which will not be re-render your component until and unless there is a change applied to a component.

const TodoList = React.memo((props: any) => {console.log(‘Todo List is Rendering’);const { list, onRemove } = props;return (<ul>{list.map((todo: any) => (<ListItem item={todo} key={todo.id} onRemove={onRemove} />) )}</ul>)})const ListItem = React.memo(({ item, onRemove } : any) => {console.log(‘Todo Item is Rendering’);return (<li>{item.name}<button type=”button” onClick={() => onRemove(item.id)}>Remove</button></li>);})

Now again test the application.

We are still getting the same output in the console. But How ? 🤔

Whenever text changes in the App component handleRemove function will redefine. Notice that we are passing handleRemove as a prop in TodoList, it will compare this prop to the previously render which causes re-rendering TodoList and ListItem. Here useCallback comes to play its role.

useCallback use to memoize a function, it takes an array of dependencies. When one of its dependency change it redefine the function.

Now I am going to wrap handleRemove function into useCallback which will prevent refining the function hence prevent re-rendering

const handleRemoveTodo = React.useCallback((id: string) => setTodos(todos.filter(todo => todo.id !== id)), [todos])

Now let's test the output again by typing input text.

Wow we have achieved our goal to prevent re-rendering by using useCallback.

Note: You can find the complete code of this article here. Please clone and try to run it and see the changes. Good Luck !

--

--

Muhammad Ata

Full Stack Javascript Engineer with a focus on React & Node.js