React hooks were introduced in React 16.8 to use State and other React Features. It lets you hook into the features without needing to create Classes.
1. useState
This might be the first hook you heard about when learning React. It is very common and can be very useful.
As the name suggests it creates a State Variable. This hook helps us to preserve the variables even after the function exits.
import React, { useState } from 'react';
const App = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
The hook returns two values: the current value
and a function
to update the value.
2. useEffect
Another common hook is the useEffect
. This is similar to the componentDidMount
in Class Components.
They are used to run side effects. They are mainly used when you want to perform any manipulations with every state change or when only a certain value changes.
Let's use the previous example and use useEffect
to log the value of count every time it changes.
import React, { useState, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count);
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
You can also return a function from useEffect
to do any clean up. It is executed when the component unmounts.
useEffect(() => {
console.log(count);
return () => {
setCount(0);
};
}, [count]);
3. useMemo
This hooks is used to keep expensive functions from running again and again. They return a memoized
value and are updated only when one of the dependencies update.
Let us consider that we have two state variables and an resource intensive / expensive function that takes one of the variables as param.
import React, { useState, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = expensiveCalculation(count);
useEffect(() => {
console.log(count);
}, [count]);
const addTodo = (todo) => {
setTodos(t => [...t, 'todo'];
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me to increase Count
</button>
<button onClick={() => addTodo("Example Todo 1")}>
Click me to set Todo
</button>
</div>
);
}
const expensiveCalculation = (num) => {
console.log("Calculating...");
// Do some Intensive Task
return num;
};
In the above example even though the calculation doesn't have anything to do with todo, we can see that the calculation is done for every render even when the count value hasn't been updated.
To fix this we can simply wrap the function in a useMemo
hook and pass in count as the dependency.
const calculation = useMemo(() => expensiveCalculation(count), [count]);
This will make sure that the calculation is calculated only when the count value gets updated.
4. useCallback
The useCallback
and useMemo
Hooks are similar. The main difference is that useMemo returns a memoized value and useCallback returns a memoized function. Let's modify the above example to create a child component to visualize the use of useCallback
better.
import React, { useState, useCallback } from 'react';
import Todos from "./Todos";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const addTodo = (todo) => {
setTodos((t) => [...t, "todo"]);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me to increase Count
</button>
<Todos todos={todos} addTodo={addTodo} />
</div>
);
};
// Todos.js
import { memo } from "react";
const Todos = ({ todos, addTodo }) => {
console.log("child render");
return (
<>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</>
);
};
export default memo(Todos);
When we try to increase the count, we can see that the Todos component is also getting re rendered. It is because every time a component gets re-rendered, its functions get recreated. Due to this the addTodo
function isn't the same function as before.
To prevent this, we can use the useCallback
hook.
const addTodo = useCallback(() => {
setTodos((t) => [...t, "New Todo"]);
}, [todos]);
Now Todos
will get re-rendered only when todos
prop changes.
5. useRef
The useRef
Hook allows you to persist values between renders.
It can be used to access a DOM element directly.
In React, we can add a ref
attribute to an element to access it directly in the DOM.
Here's a simple example to see how you can access DOM elements using useRef.
import React, { useRef } from "react";
const App = () => {
const inputElement = useRef();
const focusInput = () => {
inputElement.current.focus();
};
return (
<>
<input type="text" ref={inputElement} />
<button onClick={focusInput}>Focus Input</button>
</>
);
};
export default App;
Apart from this, there is much more you can do with hooks. You can even create your own hooks. Check this doc from the React Docs site to learn more about how you can achieve it.
If you found this article helpful, do share it with your friends.