React.js Hook Study Guide

Ilolo Izu
9 min readDec 16, 2022
React.js image from patterns.dev,

What is React.js?

First, let’s talk about the basics.

React is a popular JavaScript library for building user interfaces (UI). It is used for creating reusable UI components that can be rendered on the frontend of a web application. React is known for its efficient rendering of large data sets, making it an ideal choice for building complex and high-performance applications.

React was created by Facebook and is used by many large companies to build their web applications. It can be used in combination with other libraries and frameworks, such as Redux and React Router, to create complete web applications.

React uses a declarative approach to programming, which means that developers specify what they want the UI to look like, and React takes care of rendering the UI to the screen. This makes it easier to develop and maintain complex applications because the code is easier to understand and debug.

What is a React hook?

A React hook is a special function that allows you to “hook into” the React framework and add additional functionality to a React component. React hooks were introduced in React 16.8 and are a way to use state and other React features without writing a class.

In React, a hook is a function that enables you to add additional functionality to a component by “hooking into” one of React’s lifecycle methods or other built-in features. There are several built-in hooks in React, such as useState for adding state to a functional component, and useEffect for running side effects in a functional component.

There are several React hooks, including useContext, useReducer, useCallback, useMemo, useRef, useImperativeHandle, useLayoutEffect, and useDebugValue. However, you can also create your own custom hooks in React to extract and share behavior that is common to many components. This can help to reduce duplication of code and make your application easier to maintain. And that’s the magic of React.

React Hooks 101:

useState

useState: This hook allows you to add state to functional components. It returns a pair of values: the current state and a function that updates it.

import React, { useState } from 'react';

function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

In this example, we use the useState hook to add a count state variable to the Example component. The initial value of count is set to 0. We then use the setCount function to update the count state variable when the button is clicked. This will cause the component to re-render and display the updated value of count.

useEffect

useEffect: This hook is used to perform side effects in functional components. It is called after every render, including the first render.

import React, { useState, useEffect } from 'react';

function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

In this example, we use the useEffect hook to perform an effect after every render of the Example component. In this case, the effect updates the document title with the current value of the count state variable.

The useEffect hook is called after every render, including the first render. This is similar to the componentDidMount and componentDidUpdate lifecycle methods in class-based components.

useContext

useContext: This hook lets you access the React context in a functional component. It allows you to avoid passing props down manually through multiple levels of components.

import React, { useContext } from 'react';

// First, create a context
const MyContext = React.createContext();

// Then, create a provider
function MyProvider({ children }) {
const [state, setState] = useState({
name: 'Ilolo',
age: 25,
});

return (
<MyContext.Provider value={{ state, setState }}>
{children}
</MyContext.Provider>
);
}

// Finally, create a consumer
function MyComponent() {
const { state, setState } = useContext(MyContext);

return (
<div>
<p>Name: {state.name}</p>
<p>Age: {state.age}</p>
<button onClick={() => setState({ ...state, age: state.age + 1 })}>
Increase age
</button>
</div>
);
}

In this example, we use the useContext hook to consume the MyContext context in the MyComponent component. We use the state and setState values from the context to display and update the name and age state variables.

The useContext hook is a convenient way to access the React context in a functional component, without the need to pass props down manually through multiple levels of components.

useReducer

useReducer: This hook is similar to useState, but it lets you manage complex state logic in a reducer function. It is typically used for managing state that depends on the previous state.

import React, { useReducer } from 'react';

const initialState = {
count: 0,
};

function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);

return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>
Increment
</button>
<button onClick={() => dispatch({ type: 'decrement' })}>
Decrement
</button>
</div>
);
}

In this example, we use the useReducer hook to manage state in the Counter component. The hook takes a reducer function and an initial state as arguments, and returns the current state and a dispatch function.

We use the dispatch function to dispatch actions that update the state according to the reducer function. In this case, we have actions for incrementing and decrementing the count state variable.

The useReducer hook is a powerful way to manage complex state logic, especially when the state depends on the previous state. It is similar to the redux library, but it is built into React and can be used without additional dependencies.

useCallback

useCallback: This hook returns a memoized callback. It is useful for optimizing performance by avoiding unnecessary re-renders.

import React, { useState, useCallback } from 'react';

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

// Only re-create the callback if count changes
const increment = useCallback(() => setCount(c => c + 1), [count]);

return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>
Increment
</button>
</div>
);
}

In this example, we use the useCallback hook to create a memoized callback for the increment function. The useCallback hook takes a function and a list of dependencies as arguments, and returns a memoized version of the function.

The memoized function will only be re-created if one of its dependencies changes. In this case, the increment function only depends on the count state variable, so it will only be re-created if count changes.

This is useful for optimizing performance by avoiding unnecessary re-renders. For example, if the increment function were not memoized, the component would re-render every time the increment function is called, even if the count state variable has not changed.

useMemo

useMemo: This hook returns a memoized value. It is useful for optimizing performance by avoiding expensive calculations on every render.

import React, { useState, useMemo } from 'react';

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

// Only re-compute the value if count changes
const double = useMemo(() => count * 2, [count]);

return (
<div>
<p>Count: {count}</p>
<p>Double: {double}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}

In this example, we use the useMemo hook to memoize the double value. The useMemo hook takes a function and a list of dependencies as arguments, and returns a memoized value.

The memoized value will only be re-computed if one of its dependencies changes. In this case, the double value depends on the count state variable, so it will only be re-computed if count changes.

This is useful for optimizing performance by avoiding expensive calculations on every render. For example, if the double value were not memoized, it would be re-computed on every render, even if the count state variable has not changed.

useRef

useRef: This hook creates a mutable ref object. It is useful for keeping a reference to a value that does not change between renders.

import React, { useRef } from 'react';

function TextInput() {
const inputRef = useRef(null);

function handleClick() {
inputRef.current.focus();
}

return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>
Focus the input
</button>
</div>
);
}

In this example, we use the useRef hook to create a mutable ref object and attach it to the input element. The useRef hook takes an initial value as an argument, and returns a mutable ref object.

We use the current property of the ref object to access the current value of the ref. In this case, the current value is the input element.

This is useful for keeping a reference to a value that does not change between renders. For example, if we did not use a ref, we would not be able to call the focus method on the input element from the handleClick function.

useImperativeHandle

useImperativeHandle: This hook customizes the instance value that is exposed to parent components when using the ref prop.

import React, { useRef, useImperativeHandle } from 'react';

function TextInput({ forwardRef }) {
const inputRef = useRef(null);

useImperativeHandle(forwardRef, () => ({
focus: () => {
inputRef.current.focus();
},
}));

return <input ref={inputRef} type="text" />;
}

// Note: you must specify the forwardRef prop in the parent component
const Parent = React.forwardRef((props, ref) => (
<TextInput {...props} forwardRef={ref} />
));

In this example, we use the useImperativeHandle hook to customize the instance value that is exposed to the parent component. The useImperativeHandle hook takes a ref object and a function that returns an object of exposed values as arguments.

The exposed values are available to the parent component through the ref object. In this case, we expose the focus method of the input element, so that the parent component can call it.

This is useful for implementing advanced patterns like exposing imperative APIs to parent components. For example, if we did not use useImperativeHandle, the parent component would not be able to call the focus method on the input element.

useLayoutEffect

useLayoutEffect: This hook is similar to useEffect, but it runs synchronously after all DOM mutations. It is useful for performing calculations that need to be synchronized with the browser.

import React, { useState, useLayoutEffect } from 'react';

function Counter() {
const [width, setWidth] = useState(0);

useLayoutEffect(() => {
// Measure the width of the element
const element = document.getElementById('counter');
setWidth(element.offsetWidth);
});

return (
<div id="counter">
<p>Width: {width}px</p>
</div>
);
}

In this example, we use the useLayoutEffect hook to measure the width of the div element in the Counter component. The useLayoutEffect hook takes a function as an argument, and runs the function synchronously after all DOM mutations.

This is useful for performing calculations that need to be synchronized with the browser. For example, if we used the useEffect hook instead, the width would not be measured until after the next browser repaint, which may be too late for some applications.

useDebugValue

useDebugValue: This hook can be used to display a label for a custom hook in the React developer tools. It is useful for debugging and identifying hooks in the application.

import { useDebugValue } from 'react';

function useCounter(initialCount) {
const [count, setCount] = useState(initialCount);

// Display a label for the hook in the React developer tools
useDebugValue(`Count: ${count}`);

return [count, setCount];
}

In this example, we use the useDebugValue hook to display a label for the useCounter hook in the React developer tools. The useDebugValue hook takes a value as an argument, and displays it next to the hook in the React developer tools.

This is useful for debugging and identifying hooks in the application. For example, if you have multiple instances of the useCounter hook in your application, the labels will help you to distinguish them from each other in the React developer tools.

Custom Hooks

In React JS, a custom hook is a function that performs a specific action and can be reused throughout a React application. Custom hooks allow you to extract and share behavior that is common to many components. This can help to reduce duplication of code and make your application easier to maintain.

Custom hooks are created by defining a JavaScript function that starts with the word use and contains the logic for the specific behavior you want to reuse. You can then call this hook from any other React component in your application.

Here is an example of a custom hook in React JS that keeps track of the current time:

import React, { useState, useEffect } from 'react';

function useCurrentTime() {
const [time, setTime] = useState(new Date());

useEffect(() => {
const interval = setInterval(() => {
setTime(new Date());
}, 1000);

return () => clearInterval(interval);
}, []);

return time;
}

function Clock() {
const time = useCurrentTime();

return <div>The current time is {time.toLocaleString()}.</div>;
}

In this example, the useCurrentTime hook uses the useState and useEffect hooks to create a stateful value that updates every second with the current time. The Clock component uses this hook to display the current time.

--

--

Ilolo Izu

Software Engineer 👨🏾‍💻 | Advocating for Minorities in STEM | Track & Field World Record Holder