An Intro to React Hooks
Hooks are simply function which were introduced in React 16.8, letting us use state and other React features without writing a class. React class-based components were mandatory for any projects that require states, life-cycle methods and many other functionalities. The problems of class components like huge components, confusing classes, hard to test, etc were all solved by the React hooks.
The Hooks Convention and Rules:
- The React hooks must be imported from React.
import React, { useState, useEffect } from 'react'; - The naming convention of hooks should start with the prefix
use. We haveuseState,useEffect,useCallback,useMemo, etc. - Hooks must be called at the top level of a component, before the return statement. They can’t be called inside a conditional statement, loop, or nested functions.
- Hooks must be called from a React function (inside a React component or another hook). It shouldn’t be called from a class component and Vanilla JS function.
Built-in Hooks in React:
useState() Hook:
The useState hook is the most basic and useful React hook. It provides a state to a functional component similar to the class component.
Let’s use the above hook for toggling a dark theme. Since we only have 2 possible states either light or dark, we can consider false to be light theme while true to be a dark theme. Initially when the application loads/renders the default theme will be light.
// Theme.tsx
import React, { useState } from 'react';
const Theme = () => {
const [theme, setTheme] = useState<boolean>(false);
const switchTheme = () => {
// set the switched theme to the state or any other
business logic
// like setting it to the storage or database
setTheme(!theme);
}
return (
<>
<button onClick={switchTheme}>Toggle Theme</button>
</>
);
}
export default Theme;Whenever the user click the button, switchTheme function will be called and the theme will be switched. The switchTheme function may consist of various other business logic like setting the toggled theme to the user profile database, into the browser storage and more.
The useState hook takes an optional parameter called as initial value. Hence the state will be of type T | undefined. In the above example we are setting it to be false. Since I am using Typescript, we can set the type of the useState explicitly using generics.
This hook returns an array consisting of the state of type T and a dispatcher of type SetStateAction of type T. The state represents the current value while the setStateAction is used to change the value of the state. In the above theme is the state while setTheme is the action method. Later the setTheme(!theme); is used to change the state which will set the theme to be true for the initial click of the button. The SetStateAction is similar to this.setState({}); used in class component.
A functional component can consist of any number states defined using useState.
useEffect() Hook:
The useEffecthook is useful when we wish to run some functions during the component’s lifecycle. E.g. for updating the UI when a state changes.
The useEffect hook can be imported from react as follows:
import React, { useEffect } from 'react';
The useEffect hook aceepts two parameters, the first being the sideEffect and the second being its array of dependencies i.e. when to re-run the effect. E.g.
useEffect(() => {
// execute side Effect
// mount
return () => {
// unmount, for cleanup function
};
}, [
// array of dependencies
]);The useEffect can be used as componentDidMount , componentWillMountand componentDidUpdate lifecycle methods which are used in class components. Let’s assume a component need to set the title of the page on render .
useEffect(() => {
document.title = 'Hello World!';
}, []);The above sample code will set the title of the page to Hello World!. Here the second parameter contains an empty array stating, there are no dependencies. Hence the useEffect hook will only run once.
Similary, let’s say the component needs to set the title as the currently switched theme then.
// Theme.tsx
import React, { useState, useEffect } from 'react';
const Theme = () => {
const [theme, setTheme] = useState<boolean>(false);
useEffect(() => {
document.title = `${!theme ? 'Light' : 'Dark'} Theme`;
}, [theme]);
const switchTheme = () => {
// set the switched theme to the state or any other business logic
// like setting it to the storage or database
setTheme(!theme);
}
return (
<>
<button onClick={switchTheme}>Toggle Theme</button>
</>
);
}
export default Theme;Now the title of the page will be toggled to light and dark, depending upon the theme state value. The above function can be used as a feature provided by Helmet package, however the Helmet package does more.
useCallback() Hook:
As the name suggest, the useCallback hook is used to memoize callbacks. It will return a memoized version of the callback that only changes if one of the dependencies has changed. It is useful when passing callbacks to optimize child components that rely on reference equality to prevent unnecessary renders.
The useCallback hook aceepts two parameters, the first being the callback which needs to be memoized while the second being dependency array.
E.g.
import React, { useState, useCallback } from 'react';
interface IChildComponentProps {
count: number;
updateCount: () => void;
}
const ChildComponent: React.FC<IChildComponentProps> = ({ count, updateCount }) => {
return (
<div>
<button onClick={updateCount}>Child Component {count}</button>
</div>
);
}
const CallbackHook = () => {
const [count, setCount] = useState<number>(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<>
<ChildComponent count={count} updateCount={handleClick} />
</>
);
}
export default CallbackHook;In the above code, the ChildComponent is receiving two props, the count and the updateCount .
useMemo() Hook:
React components may require to perform expensive calculations like filtering an array of list for a certain search query, performing a big calculation for each array of objects, etc. The above operations will be carried out in every render of the component which will drastically decrease the performance of the application. However to fix those memoization technique can be used.
useMemo is a built-in React hook which is similar to useCallback hook instead it allows to apply memoization to any value type. It accepts two parameters the first being the function that computes the result and the second being the array of dependencies. E.g.
const memorize = useMemo(() => {
// do something expensive
}, [
// array of dependencies
]);A simple component for filtering a list of students by their name.
import React, { useState, useMemo, useEffect } from 'react';
interface IStudent {
name: string;
age: number;
}
const useMemoHook = () => {
const [students, setStudents] = useState<IStudent[]>();
const [search, setSearch] = useState<string>('');
useEffect(() => {
// fetch the students from an api and set to the student state
}, []);
const filterByName = () => {
// filter the student list
return students?.filter((student: IStudent) => student.name.includes(search));
}
const memorize = useMemo(filterByName, [search]);
return (
<>
<input type={'text'} value={search} onChange={(e) => setSearch(e.target.value)} />
</>
)
}
export default useMemoHook();In the above code, the useMemo hook will only run when the search state changes, i.e. on user input. And the expensive function filterByName will be executed which will return an array of students which matches the search query.
Memoization can improve performance of the application, as well as drastically decrease the performance when used incorrectly. The components need to be profiled with and without the memoization hooks.
Custom hooks:
Custom hook allows us to extract some component logic into a reusable function. As per the React hooks convention and rules, it must start with a use keyword.
Here is a sample code for creating a custom hook for toggling the theme. The hook accepts a parameter of type IDefaultTheme which must be either ‘light’ or ‘dark’. The passed value is used as a default theme, however the paramter is optional as the default value is ‘light’. The hook returns the current theme and a function to toggle the theme as an array similar to the useStatehook.
import React, { useState } from 'react';
// useTheme.tsxtype IDefaultTheme = 'light' | 'dark';
const useTheme = (defaultTheme: IDefaultTheme = 'light') => {
const [theme, setTheme] = useState(defaultTheme);
const toggleTheme = () => {
// business logic
setTheme(theme === 'light' ? 'dark' : 'light');
};
return [theme, toggleTheme];
}// App.tsx
const App = () => {
const [theme, toggleTheme] = useTheme();
return (
<>
</>
);
}The same custom hooks in two components will not share data. Each custom hook create a new function that is using useState and useEffect from React. Hence we can use several hook inside the same component.
The above codes are all available here: https://github.com/shresthadeepesh/react-hooks-basic