Understanding React Hooks: How to Use useRef, useMemo, and useCallback for More Efficient Code
Tags: react, hooks, webdev, tutorial
Published: 2023-02-22 21:03:57

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:

```javascript

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:

```javascript

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:

```javascript

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:

```javascript

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](https://linktr.ee/michael_osas) to read more about JavaScript, React, Node.js, and more…!