In this article we are going to learn about react Hooks, What are the types of hooks, built-in hooks, how you can combine multiple hooks to create your own hooks
What are Hooks?
To understand react hooks, let us first consider what are functional components?
Functional components at their core are functions, and as with all javascript functions you can use one function inside of another function.
Hooks are functions that let you use different react features in your components. You can import built-in hooks or create your own custom hooks by combining one or more built in hooks
This article is brought to you be DeadSimpleChat, Chat API and SDK for website and Apps. Using which you can easily add in-app messaging to your app
Let us learn about built in hooks and how you can combine them to create your own hooks with examples
Types of built-in hooks
State Hooks
useState
useReducer
Context Hooks
- useContext
Ref Hooks
useRef
useimperativehandle
*Effect Hooks
*
useEffect
useLayoutEffect
useInsertionEffect
Performance Hooks
useMemo
useCallback
useTransition
useDeferredValue
Other Hooks
useDebugValue
useId
useSyncExternalStore
Creating your own Hooks
State Hooks
React State basically lets a component remember the data, any data that you would like to store can be stored in State
State is a javascript Object, that stores the data that is specific to the component inside of which the state is defined.
To add State
to a component, use one of the following hooks
useState
declares a state variable which can be assigned to any data and updated directlyuseReducer
declares a state variable inside a reducer function with update logic
Now, let us look at some of the examples
Example 1:
initializing the state
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// rest of the component
}
Here we are initializing state using the useState
hook. The hook is a function that takes the initial state as an argument and returns an array with current state and a function to update the current state
accessing the state
To access the state just refer to the variable that you have defined while initializing the state, like so
<p>
the count = {count}
</p>
updating the state
simply call the update function and assign the new value of state to the function
setCount(count + 1);
Note: The component re-renders whenever the state is updated
Example 2:
Let us look at another example with useReducer
. When you have many state updates across many event handlers it can get overwhelming.
In these cases you can consolidate all the state logic outside your component in a single function called the reducer
Reducer is a pure function that takes the current state and an action then returns the new state
import React, { useReducer } from 'react';
// current state and action are recieved and new state is returned
const counterReducerFunc = (state, action) => {
switch (action.type) {
case 'increase':
return { count: state.count + 1 };
case 'decrease':
return { count: state.count - 1 };
default:
throw new Error(`This action is not known: ${action.type}`);
}
};
const Counter = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increase' })}>Add</button>
<button onClick={() => dispatch({ type: 'decrease' })}>Subtract</button>
</div>
);
};
export default Counter;
Context Hooks
Context lets you pass the data from far away (distant ) parents to child with passing it as props
As an example, Your App's top level component can pass the current UI theme to all components below no matter how deep.
With react context you are able to avoid what is called as 'prop drilling' where you passing down the data from one component to its child, then from child component to its child component
With context all the data is provided to every component that subscribes to the context
-
useContext
reads and subscribes to a context
Example
Let us understand the useContext
with the help of an example
First, we are going to create a context by using React.createContext()
method
const SomeContext = React.createContext();
Providing value to react context
The SomeContext.Provider
component is used to provide a context value to all the children in the component tree
You can then place the SomeContext.Provider
anywhere in the tree where you need to access the value
Consuming the Context value with useContext
To access the value from context simply use the useContext Hook inside your component
useContext
takes the context Object that is 'SomeContext' here and returns the current context value like so
const ChildComponent = () => {
const currentContextValue = useContext(SomeContext);
return <p>{CurrentContextValue}</p>; // "We have electricity"
};
useContext
provides the context value directly to the child components without the need to pass the data using props.
Ref Hooks
The Refs lets a component hold a value that is not needed for rendering.
This value can be a DOM node or a timeout ID. Unlike a state updating a ref does not re render a component.
Refs are sort of an escape hatch from the react paradigm,
Refs can be useful when working with non react systems like Rest APIS, or to store previous state values, accessing the DOM directly or keeping any mutable values.
There are two hooks that deal with refs
-
useRef
declares a ref and you can hold any value in it -
useImperativeHandle
lets you customize the ref exposed by the component. This hook is rarely used
Let us look at some examples to better understand this
Example
Creating a Ref
you can initialize the ref like this
const exampleRef = React.useRef();
The ref object here that is exampleRef will persist throughout the lifetime of the component
accessing the values inside of a Ref
You can easily access the values inside of the Ref by using the .current property like
exampleRef.current = "We got the power";
console.log(exampleRef.current); //outputs "We got the power";
Accessing DOM elements
In this example we are going to access a DOM node directly for something that can't be done in react declarative
lets see the example:
import React, { useRef } from 'react';
const SampleComponent = () => {
const inputRef = useRef();
const handleClick = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>On button click focus on the input tab</button>
</div>
);
};
export default SampleComponent;
In this example we have input element to the inputRef ref and when the handleClick function is called it focuses on the input element using the inputRef.current.focus()
method
Effects Hooks
Effects lets a component connect to and synchronize with external systems
This includes dealing with DOM, widgets that were written using a different UI library and other non react code.
Basically, useEffect
lets you perform side effects in functional components. This includes interaction with outside of the component like data fetching, subscriptions, manual DOM manipulations etc.
Effects are an escapse hatch from the react ecosystem. So, do not use effects to orchestrate the data flow of your application as this might defeat the basis of using react and create multiple problems.
If you are not interacting with systems outside of React, you might not need to use Effect.
Let us look at examples to better understand the useeffect hook:
import React, { useEffect } from 'react';
const CoolComponent = () => {
useEffect(() => {
console.log('Rendering the component on the display');
});
return <h1>using the useEffect </h1>;
};
export default CoolComponent;
In this example when the CoolComponent is rendered, the console.log statement written within the function will print the 'Rendering the component on the display'
Now, let us look at two other variations of useEffect
useLayoutEffect
and
useInsertionEffect
useLayoutEffect
runs similarly to the useEffect
but runs synchronously after all the DOM mutations are done. This makes it a good tool for reading the layout from the DOM and re-rendering
So, if you need to measure a DOM node and then cause a synchronous re render, you can use the useLayoutEffect
useInsertionEffect
is a react hook designed for developers working on CSS in JS libraries
Using this hook you can inject styles into the DOM before layout effects are fired, ensuring that these styles are available immediately when the component renders
the useInsertionEffect
accepts a setup function and an optional dependency array similar to other hooks
Performance Hooks
Skipping unnecessary work is a common way to optimize re-rendering performance.
That is if the data has not changed from the previous re-render, you can tell react to skip the re-render or to skip the calculations thus saving resources
React provides several hooks to optimize the performance of your components and these are:
To skip calculations and avoid re-rendering of components you can use
useMemo
:Lets you cache the result of an expensive calculationuseCallback
: Lets you cache a function definition before passing it down to an optimized component
Sometimes, you can't skip the re-rendering because the screen needs to update.
In this case you can improve performance by separating the blocking updates that must be synchronous from non-blocking updates which do not need to block the UI
In these cases you can use one of the following hooks
useMemo
useMemo
memorizes the result of a function. let us learn more about this using an example
import React, { useMemo } from 'react';
const CoolComponent = ({ values }) => {
const expensiveCalculation = useMemo(() => {
return values.reduce((big, expensive) => big + expensive, 0);
}, [values]);
return <div>{expensiveCalculation}</div>;
};
export default CoolComponent;
In the above example useMemo
memorizes the result of the expensiveCalculation
and only perform the calculation if the props change
useCallback
useCallback
is similar to useMemo but it returns a memorized version of the callback function that only changes if one the dependencies have changed
let us consider an example
import React, { useState, useCallback } from 'react';
const CoolComponent = () => {
const [count, setCount] = useState(0);
const increaseTheCount = useCallback(() => {
setCount(count + 1);
}, [count]);
return <button onClick={increaseTheCount}>Increase the Count</button>;
};
export default CoolComponent;
Here we are using the useCallback
function to create a memorized version of the increaseTheCount
function that only changes when the count changes
useTransition
useTransition
is a hook that manages transitions on slow networks and devices.
It returns two values
a
startTransition
function andan
isPending
boolean
Let us consider an example
import React, { useState, useTransition } from 'react';
const CoolComponent = () => {
const [resource, setResource] = useState(null);
const [startTransition, isPending] = useTransition();
const GettingTheResource = () => {
startTransition(() => {
fetchResource().then(setResource);
});
};
return (
<div>
<button onClick={GettingTheResource}>Get the Resource</button>
{isPending ? 'Waiting...' : <ShowTheResource resource={resource} />}
</div>
);
};
export default CoolComponent;
In the above example the usetransition
is used to provide a smooth transition when the app is fetching a resource.
The isPending
value can be used to show a loading screen while the resource is being fetched over the internet
useDeferredValue
Lets you defer a non-critical part of the UI and lets the other parts display first
Let us consider an example to better understand this
import React, { useState, useDeferredValue } from 'react';
const CoolComponent = () => {
const [writeUp, setWriteUp] = useState('');
const deferredValue = useDeferredValue(writeUp);
return (
<div>
<input
value={writeUp}
onChange={e => setWriteUp(e.target.value)}
/>
<SlowComponent setWriteUp={deferredValue} />
</div>
);
};
export default MyComponent;
In this example the useDeferredValue
is used to defer the value of the writeUp as that is less important than showing the input
tab to the user first
Other Hooks
These are some of the other interesting built-in hooks available
useDebugValue
You can customize the label that the React DevTools displays for your custom with with useDebugValueuseId
Associate a unique ID with a component. You can use this with accessibility APIsuseSyncExternalStore
subscribe a component with an external store
Let us consider some examples to understand these hooks better
useDebugValue
import { useState, useDebugValue } from 'react';
function useChatStatus(userId) {
const [isOnline, setIsOnline] = useState(null);
// show a lable next to this hook in react Dev tools
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
In the above example we are using useDebugValue
to show a custom hook useChatStatus
in the react devtools
useId
useId
is a react hook that can be used to create multiple unique ids that can be passed to accessibility attributes
let us consider an example to learn better
import { useId } from 'react';
function PasswordField() {
// useId is used to generate a unique ID
const passwordHintId = useId();
return (
<>
<label>
Password:
<input type="password" aria-describedby={passwordHintId} />
</label>
<p id={passwordHintId}>
The password should contain at least 18 characters
</p>
</>
);
}
export default function App() {
return (
<>
<h2>Choose password</h2>
<PasswordField />
<h2>Confirm password</h2>
<PasswordField />
</>
);
}
In the example the useId
is used to generate unique ids for the description paragraph of the password input. If the PasswordField
is used multiple times across the app the generated ids will not clash
Reason behind using useId
for generating ids instead of an incrementing counter
you could simply increment the Global counter like count++
to generate new ids
The primary reason is that the useId
maintains compatibility with the server rendering.
Creating custom hooks
You can also define your own custom hooks as javascript functions.
This involves extracting the duplicated logic that is being re used in multiple components into a separate function
The function will be called a Hook. The name of the hook always starts with the word use
There can be multiple hooks within a hook, like there can be multiple functions inside of a function
The returned values can be anything like functions, state, or any other value that you want to share between components
let us create the useOnlineUserStatus
Hook as an example to better understand this topic
What is the Shared Logic In this case the shared logic is whether the user is online or not. This shared logic can be useful to multiple components
Create a function with the starting keyword use As we have already decided that the name of our hook will be useOnlineUserStatus
.
function useOnlineUserStatus() {
}
Moving the shared logic into the function that we just created
import { useState, useEffect } from 'react';
function useOnlineUserStatus() {
const [isUserOnline, setIsUserOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsUserOnline(true);
}
function handleOffline() {
setIsUserOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isUserOnline;
}
return Here we are returning the data the other components might need that is the status of the user.
Using the Custom Hook in your code
You can use the custom hook in your component like any other hook
function ShowOnlineUserStatus() {
const isUserOnline = useOnlineUserStatus();
return <h1>{isOnline ? 'โ
Online' : 'โ Disconnected'}</h1>;
}
Thus we have created a custom hook
Conclusion
In this article I have covered a wide range of topics regarding the React Hooks, including types of hooks their use cases and how to use them in your code
I hope that you liked the article. Thank you for reading
Top comments (1)