How to use React Hooks

Tomohiro Meo
8 min readApr 20, 2021

What I show you about React hooks in this article

Basic hooks

  • 1. useState
  • 2. useEffect
  • 3. useContext

Other official React hooks

  • 4. useRef
  • 5. useReducer
  • 6. useMemo
  • 7. useCallback

What is React Hooks?

This is a new feature added in React 16.8.

Using React hooks, state management and lifecycle methods can be implemented in function components without writing classes.

Advantages of React hooks

  • Reduce the number of class components that tend to become complex.
  • You don’t need to do bucket brigade to pass props.
  • It’s easier to share state between components.

1. useState

The state refers to the “state” that the component holds internally, such as the data displayed on the screen, and the state that the application holds.

“State” can be changed later unlike props.

How to use useState

In useState, it is necessary to pass the initial value of the state as an argument. In the following code, the useState(0) is the initial value of the state.

const [count, setCount] = useState(0);

If the state is a string, an empty string may be passed, or null may be passed.

const [name, setName] = useState('');const [isOnline, setIsOnline] = useState(null);

When using a variable as the initial value of useState

The value to be passed to useState can be a variable, so we can use a variable called initialState and write like the following.

const initialState = 0const [count, setCount] = useState(initialState)

Names like count and setCount can be set freely, but the name of the setter function is often used as “set+state name”.

Sample code using useState (Counter)

where count is the value of the state and setCount is the setter function.

import React, { useState } from "react";
export default () => {
const [count, setCount] = useState(0);
return (
<div>
<p>clicked {count}times</p>
<button onClick={() => setCount(count + 1)}>Click!</button>
</div>
);
};

2. useEffect

useEffect is a hook that allows you to do the same thing as a lifecycle method without using React’s lifecycle methods (such as componentDidMount), but without leaving the function component.

In the image, useEffect is a combination of the following three lifecycle methods.

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

When you should use useEffect?

The useEffect is useful when you want to add some processing after React has rendered, such as when the React application is loaded or when a component’s state value changes, as in the following example.

  • Initialization timing
  • Timer
  • Checkbox On/Off
  • Manipulating the DOM in a component
  • HTTP request

How to use useEffect

Basic code about useEffect

import { useEffect } from 'react'
useEffect(() => {
// ex). HTTP request
return () => {
// clean up function
};
}, []);

Using useEffect, update the title tag every time the button is clicked.

Let’s use useEffect to create a function that will update the value of the title tag when the button is clicked.

import React, { useState, useEffect } from "react";
export default () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Cliked ${count} times`;
}, [count]);
return (
<div>
<p>Cliked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click</button>
</div>
);
};

In the past, when manipulating the DOM, you had to use componentDidMount and add processing after the DOM was drawn.

With useEffect, you can wait for the DOM to be drawn before manipulating it without using the class-based componentDidMount.

How to call effect only on the first load

If you want to call the effect only on the first call, leave the contents of the second argument (dependent array) empty.

By passing an empty array as the second argument, the effect will not be called during other rendering. This is useful when initializing.

// Call effects only on initialization
useEffect(() => {
console.log('only on initialization')
}, []);
// effect will be called every time.
useEffect(() => {
console.log('call effect every time')
});

To specify when to call the effect

In the above example, the effect was called only on the first call, but you can call the effect at other timing as well.

The way to do this is just putting the state or function in the second argument.

In this way, the effect can be called when the value passed in the argument is changed.

// The effect is called when the value of count changes.
useEffect(() => {
document.title = `Cliked ${count}times`;
}, [count]);
// The effect will be called every time.
useEffect(() => {
document.title = `Cliked ${count}times`;
});

Note that if you don’t pass any value for the second argument, the effect will be called every time you update the file.

To add processing when dismounted

In useEffect, you can add a dismount process similar to the componentWillUnmount lifecycle method.

To add the dismounting process, use return in the useEffect.

In this way, you can execute the process when the mount is dismounted.

useEffect(() => {
console.log('Completed')
return () => {
console.log('dismounted')
}
}, []);

For processing when the mount is released, you can write clearInterval and other methods that are used in setInterval when using React.

import React, { useState, useEffect } from "react";
export default () => {
const [timer, setTimer] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setTimer((timer) => timer + 1);
}, 1000);
return () => {
clearInterval(interval);
};
}, [timer]);
return <p><strong>{timer}</strong> seconda passed</p>;
};

useEffect is recommended over useLayoutEffect.

There is also an official React hook called useLayoutEffect that is similar to useEffect. The useLayoutEffect may slow down the application, so it is better to use the useEffect.

3. useContext

When you try to share state in a React application, you have to pass props and it’s troublesome, but useContext makes it easier to share state and data.

When you should use useContext?

It is also used when you want to manage state in one place, but the project is not large enough to use Redux.

Sample code

Let’s implement a function that uses context to switch between dark and light modes.

ⅰ. createContext()

To access contexts with the useContext hook, you use createContext.
crateContext takes a context object and returns the current context.

import React from "react";
import { useState, useContext } from "react";
const themes = {
light: {
color: "#222",
backgroundColor: "#eee",
},
dark: {
color: "#ddd",
backgroundColor: "#333",
},
};
export const ThemeContext = React.createContext(themes.dark);

ⅱ. Surrounding App components with Providers

To use the themes context throughout the application, enclose the App component in ThemeContext.

import { ThemeContext } from './ThemeContext';
const App = () => {
return (
<ThemeContext.Provider value={themes.dark}>
// ...
</ThemeContext.Provider>
);
};

ⅲ. Use context

To use a context created in a component, useContext or ThemeContext.

import { useContext } from "react";
import { ThemeContext } from './ThemeContext';
const LoremIpsum = () => {
const theme = useContext(ThemeContext);
return (
<div style={{ backgroundColor: theme.backgroundColor }}>
<p style={{ color: theme.color }}>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Explicabo,
quis!
</p>
</div>
);
};

4. useRef

useRef is a hook that allows you to access React elements and DOM nodes generated by the render method.
This is useful when you need access to the actual DOM, such as when focusing on an element.

Example code

The code below will focus on the input form when the button is clicked.

import React, { useRef } from "react";
export default () => {
const textInput = useRef(null);
const focusInput = () => textInput.current.focus()
return (
<div className="App">
<input type="text" ref={textInput} />
<button onClick={focusInput}>Edit</button>
</div>
);
};

CreateRef can implement the same functionality as useRef

The same kind of functionality can be implemented with createRef. If we rewrite the above code in createRef, it will look like this

import React, { createRef } from "react";
export default () => {
const textInput = createRef();
const focusInput = () => textInput.current.focus();
return (
<div className="App">
<input type="text" ref={textInput} />
<button onClick={focusInput}>Edit</button>
</div>
);
}

What is the difference between useRef and createRef?

While createRef returns a new reference on every render, useRef returns the same reference every time. Therefore, useRef allows the component to have the same value in .current when it is rendered again.

5. useReducer

useReducer has the ability to declare the state of a function component.

There are two ways to declare states: useState and useReducer.
useReducer is more suitable for declaring states with complex logic involved.

useReducer is used as follows. The reducer is a function that takes the current state and action and returns a new state, and the initialState is the initial value of the state.

const [currentState, dispatch] = useReducer(reducer, initialState);

The point of useReducer

  • The essence of useReducer is that state-dependent logic can be expressed as a state-independent function object (dispatch).
  • This leads to performance improvements with React.memo.
  • To take advantage of useReducer, pack as much logic as possible into a reducer by consolidating state into one.

When useReducer can improve performance

The use of React.memo is likely to have a significant effect in improving the performance of React apps. Utilizing this to avoid unnecessary re-rendering of components is the basic performance tuning of React apps.

It’s a long story to show example code, so I would like to write another example about useReducer in another article soon.

6. useMemo

useMemo is a hook that will prevent the specified function from being called on every render.

Since useMemo performs the calculation only once in the first rendering and returns the cached value in all other renderings, it is usually used for optimization of functions that are computationally intensive.

How to use useMemo

import React, { useMemo } from 'react';
const example = useMemo(() => expensiveCalculation(a), [a]);

As with useEffect, the dependency array of useMemo can be empty, or it can contain multiple arguments.

const example = useMemo(() => expensiveCalculation(a, b), [a, b]);

7. useCallback

useCallback is preventing unneeded rendering by passing a dependency array.

how to use useCallback

useCallback passes a callback function inline, just like useEffect and useMemo.

import React, { useCallback } from 'react'
const example = useCallback(() => expensiveCalculation(a, b), [a, b]);

Dependent arrays are required for useCallback even if there are no arguments

For useCallback, like useEffect, if you don’t pass a dependent array, a new value will be returned with each call, so pass an empty array even if you don’t use any arguments in the callback.

const example = useCallback(() => expensiveCalculation(), []);// It returns a new value every time.
const example = useCallback(() => expensiveCalculation());

Difference between useMemo and useCallback

A similar hook to useCallback is useMemo, but the difference between useCallback and useMemo is as follows.

Although useMemo and useCallback are similar in purpose and usage, useCallback returns memoized callback functions instead of values.

  • useMemo returns a value, useCallback returns a callback function
  • useCallback renders when an element of the dependent array is changed

useMemo returns a value, useCallback returns a callback function

Although useMemo and useCallback are similar in purpose and usage, useCallback returns memoized callback functions instead of values.

Therefore, if you need to perform a large amount of calculations, it may be more efficient to use useMemo, which caches the calculation results, rather than useCallback.

useCallback renders when an element of the dependent array is changed

Both useCallback and useMemo pass elements to a dependency array, but unlike useMemo, useCallback is re-rendered and returns a callback function when the elements of the dependency array are changed.

--

--