React Hooks are a feature in React that allows developers to use state and other React features without needing to write a class component. They have revolutionized the way developers write React components, making them more reusable and maintainable. These hooks provide a simpler way to use state and other React features, without requiring a class component. This post explores the details of three built-in hooks in React: useRef, useMemo, and useCallback. They make it easier to reuse the same stateful logic across different components, which makes code more maintainable and easier to understand.
It took me the whole day to come up with this detailed explanation of these hooks, so get ready to take breaks while reading this post... 👍🏼
There are different built-in hooks in React, such as useState, useEffect, useRef, useMemo, and useCallback. These hooks are functions that can be used inside functional components to manage state, perform actions, and access context.
useRef is a hook that provides a way to store a mutable value that persists across renders. It is commonly used to access or modify the properties of DOM elements, like the value of an input or the scroll position of a container. Unlike useState, updating the value of a useRef does not trigger a re-render of the component. Instead, the value can be accessed or modified directly by using the current property of the returned object.
useMemo is a hook that memoizes the result of a function and returns a cached value unless one of the dependencies has changed. This can be useful for expensive computations or when a component's rendering depends on the value of a complex data structure. The second argument of useMemo is an array of dependencies, and the function is only re-executed when one of the dependencies changes.
useCallback is a hook that memoizes a function and returns a cached value unless one of the dependencies has changed. This can be useful for optimizing the performance of a component by avoiding unnecessary re-renders caused by passing a new function reference to child components. The second argument of useCallback is an array of dependencies, and the function is only re-created when one of the dependencies changes.
In general, React Hooks make it easier to write more reusable and maintainable code by breaking down stateful logic into smaller, composable functions. Using hooks can simplify the process of writing components, reduce repetitive code, and make it easier to understand the behavior of a component.
Alright, Let’s Take a Deeper Dive into These Hooks:
1. useRef
In React, the useRef
hook is used to create a mutable reference that can persist across re-renders. It is similar to the ref attribute in class components, but with some differences.
The useRef
hook takes an initial value as an argument and returns a mutable ref object with a current property. The current property can be set and accessed directly, without triggering a re-render of the component. This is useful for storing a value or a reference to a DOM element that needs to be accessed later.
Here's an example of how to use useRef
to store a reference to an input element and focus it when a button is clicked:
import { useRef } from 'react';
function Example() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
This is a React functional component called Example that uses the useRef
hook to create a reference to an input element in the component. Let's take a closer look at the code:
The useRef
hook is a built-in React hook that creates a mutable reference to an element or a value that persists across component renders. In this example, the useRef
hook is used to create a reference to an input element by calling useRef(null)
. The inputRef
variable is then initialized to this reference.
The handleClick function is called when the user clicks the "Focus Input" button. The function uses the current property of the inputRef object to get a reference to the input element and then calls the focus()
method to focus the input element.
The return statement of the Example component renders a <div>
element that contains an input element and a button. The ref attribute of the input element is set to inputRef
, which creates a reference to the input element. The onClick
attribute of the button is set to handleClick, which calls the handleClick function when the button is clicked.
In summary, this example demonstrates how to use the useRef hook in a React functional component to create a reference to an input element, and then use that reference to focus the input element when a button is clicked. By using the useRef hook, the reference to the input element persists across component renders, which can be useful for accessing and modifying the input element in other parts of the component or application.
2. useMemo
useMemo
is a React Hook that is used for performance optimization by memoizing the result of a computationally expensive function. Memoization is the process of caching the result of a function based on its arguments, so that if the function is called again with the same arguments, the cached result is returned instead of recomputing the result.
The useMemo
Hook takes two arguments: a function that returns the memoized value, and an array of dependencies. The function is only re-executed when one of the dependencies changes. If the dependencies are the same between renders, the previously memoized value is returned
Here is a detailed example of the useMemo
hook:
import React, { useMemo } from 'react';
function MyComponent({ a, b }) {
const memoizedValue = useMemo(() => {
// Compute the result only when a or b changes
return a * b;
}, [a, b]);
return (
<div>
Memoized Value: {memoizedValue}
</div>
);
}
In the code example above, useMemo
is being used to cache or remember the result of a calculation involving a and b. This means that if the values of a or b don't change between renders, the memoized value will be returned instead of re-computing it, which can improve performance by saving computation time.
However, it's important to note that using useMemo is not always necessary. If the calculation being cached is not very time-consuming or if the component re-renders frequently regardless of the memoized value, the performance benefits of using useMemo may not be significant, or it may even make the performance worse. It's also important to keep in mind that memoization is not always the best approach, especially when the value being cached is likely to change frequently. In such cases, it may be better to calculate the value directly in the render function.
Overall, useMemo
is a useful tool for optimizing the performance of a React application, but it should be used thoughtfully and with an understanding of the potential benefits and tradeoffs.
3. useCallback
useCallback
is a built-in React hook that is used to optimize performance by reducing unnecessary re-rendering of components. The hook memoizes a function, which means that it returns the same function instance on re-renders unless any of its dependencies change.
Here is the basic syntax of useCallback:
javascript
const memoizedCallback = useCallback(
() => {
// function body
},
[/* dependency array */]
);
It looks like the useEffect
hook, the first argument of useCallback
is the function that you want to memoize. The second argument is an optional array of dependencies that are used to determine whether the memoized function should be recomputed. If any of the dependencies change, the memoized function will be re-computed; otherwise, the cached function will be returned. The primary benefit of useCallback
is that it helps to prevent unnecessary re-renders of child components.
Let’s take a simple application as case study:
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={handleClick}>Increment count</button>
<ChildComponent />
</div>
);
}
function ChildComponent() {
const [value, setValue] = useState(0);
const expensiveFunction = () => {
// do some expensive computation here
};
useEffect(() => {
expensiveFunction();
}, [expensiveFunction]);
return <div>{value}</div>;
}
This is an example of a React functional component that consists of two components: ParentComponent and ChildComponent. Let's take a closer look at each component:
The ParentComponent: This component defines a state variable called count using the useState
hook, which is initialized to zero. It also defines a click event handler function called handleClick, which increments the count state variable using the setCount function.
When the component is rendered, it returns a <div>
element with a button that, when clicked, calls the handleClick function to increment the count variable. It also renders another component called ChildComponent.
The ChildComponent: This component defines a state variable called value using the useState
hook, which is initialized to zero. It also defines a function called expensiveFunction, which simulates some expensive computation.
This component uses the useEffect
hook to call the expensiveFunction function when the component is mounted. The useEffect
hook takes two arguments: a function and a dependency array. In this case, the function is expensiveFunction, and the dependency array is [expensiveFunction]. By specifying [expensiveFunction] as a dependency array, the useEffect
hook will only re-run the expensiveFunction function when the expensiveFunction reference changes.
Finally, the component returns a <div>
element with the value state variable rendered inside it. This example demonstrates how to use the useState
and useEffect
hooks in React functional components to manage state and side effects, respectively. The ParentComponent component updates its count state variable in response to a button click, while the ChildComponent component uses the useEffect hook to run some expensive computation when the component mounts.
To optimize this code, we are going to use useCallback
to memoize the expensive function and only recompute it when its dependencies change:
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
const memoizedFunction = useCallback(() => {
// do some expensive computation here
}, []);
return (
<div>
<button onClick={handleClick}>Increment count</button>
<ChildComponent expensiveFunction={memoizedFunction} />
</div>
);
}
function ChildComponent({ expensiveFunction }) {
const [value, setValue] = useState(0);
useEffect(() => {
expensiveFunction();
}, [expensiveFunction]);
return <div>{value}</div>;
}
In this code example, the ParentComponent is similar to the previous example, except that it uses the useCallback
hook to define a memoized function called memoizedFunction. The useCallback
hook is used to memoize a function so that it doesn't get recreated on every render. This can be useful when passing a function as a prop to a child component, as it can prevent unnecessary re-renders. When the component is rendered, it returns a <div>
element with a button that, when clicked, calls the handleClick function to increment the count variable. It also renders another component called ChildComponent and passes the memoizedFunction as a prop.
The ChildComponent is also similar to the previous example, except that it receives a prop called expensiveFunction and uses it to run the expensive computation using the useEffect
hook. Since the expensiveFunction is passed as a prop, it is also passed down from the ParentComponent, which memoizes the function using the useCallback
hook. In this case, the useEffect
hook is set up to run the expensive Function when the component is mounted or when the expensive Function reference changes. By memoizing the function in the ParentComponent, the expensive Function reference will not change on every render, which can prevent unnecessary re-renders of the ChildComponent.
Finally, the component returns a <div>
element with the value state variable rendered inside it.
To put it simply, this example shows how to use a technique called useCallback
in React functional components to make sure that a function is not re-run unnecessarily. It also shows how to use a function that has been optimized in this way as a prop to a child component and how to run a time-consuming task using the useEffect
technique.
My final thoughts
In conclusion, React Hooks provide developers with an easier way to write and manage stateful logic in functional components.
The useRef
hook creates a mutable reference to an element or a value that persists across renders, allowing for direct access and modification without re-rendering the component. The useMemo
hook is used for performance optimization by caching the result of a computationally expensive function, only re-executing the function when the dependencies have changed. The useCallback
hook is useful for optimizing the performance of a component by memoizing a function and returning a cached value unless one of the dependencies has changed.
With built-in hooks such as useRef
, useMemo
, and useCallback
, developers can manage state, perform actions, and access context more efficiently. By breaking down stateful logic into smaller, composable functions, hooks make it simpler to write components, reduce repetitive code, and make component behavior easier to understand. The useRef
hook can create a mutable reference to an element or value that persists across component renders. The useMemo
hook can optimize performance by memoizing the result of a computationally expensive function, and the useCallback
hook can memoize a function to optimize the performance of a component. Overall, using React Hooks is a great way to make code more reusable and maintainable.
Thank you for reading...
I hope you found this little article helpful. Please share it with your friends and colleagues. Connect with me on my socials to read more about JavaScript, React, Node.js, and more…!
Top comments (3)
Great explanations. For sure React changed the way we use it when it released its hooks, much more easier than work with classes, don't you think?
You explained useCallback() beautifully, however the examples about it were a bit confusing. My question is: why did you use useEffect() to execute the memoized function? I mean, Why does it need to be invoked after child component is mounted if it can also be executed during the child rendenring?
Great job for publishing such a beneficial web site. Your blog is useful . Ipl id satta
Thanks for the warm comment. 😊