useState
The useState
hook is used to add state to a function component. This hook must be called from inside a component or another hook.
import { useState } from 'react';
const [state, setState] = useState(initialState);
The useState
hook returns an array with two values. The first value is the current state and the second value is a function that you can use to update the state. The initialState
is the initial value of the state.
This hook should be used when you want to add state to a function component. For example if you want to add a counter to a component you can use the useState
hook.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
If you want to add a form to a component you can use the useState
hook to store the form values.
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</form>
);
}
If you want to add a toggle to a component you can use the useState
hook to store the toggle value.
function Toggle() {
const [isToggled, setIsToggled] = useState(false);
return (
<div>
<h1>{isToggled ? 'ON' : 'OFF'}</h1>
<button onClick={() => setIsToggled(!isToggled)}>Toggle</button>
</div>
);
}
The useState
hook can only be used on client side components. If you want to add state to a server side component you can use the useState
hook in a custom hook and then use the custom hook in the server side component.
function useCounter() {
const [count, setCount] = useState(0);
return [count, setCount];
}
useEffect
The useEffect
hook is used to watch for external changes and run side effects. This hook must be called from inside a component or another hook.
import { useEffect } from 'react';
useEffect(() => {
// Run side effect
return () => {
// Clean up
};
}, [dependencies]);
The first parameter is the side effect that you want to run and the second parameter is an array of dependencies. If any of the dependencies change the side effect will be re-run.
This hook should be used when you want to run side effects in function components. For example if you want to fetch data from an API when the component mounts you can use the useEffect
hook.
function Profile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then((data) => setUser(data));
}, [userId]);
return <h1>{user.name}</h1>;
}
An external change is anything that is outside of react this can be another system or a browser API. For example if you want to display a message when users are offline you can use a browser API to check if the user is online.
function Status() {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
}, [friendID]);
return isOnline;
}
If you want to set a function on a timer you can use the useEffect
hook to set the timer when the component mounts and clear the timer when the component unmounts.
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);
return <h1>{count}</h1>;
}
If you want to response to a window mouse event you would use useEffect
to add the event listener when the component mounts and remove the event listener when the component unmounts.
function Mouse() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
function handleMouseMove(event) {
setPosition({
x: event.clientX,
y: event.clientY,
});
}
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return <h1>{position.x}, {position.y}</h1>;
}
useLayoutEffect
The useLayoutEffect
hook is similar to the useEffect
hook but it runs synchronously after the DOM has been updated. This hook must be called from inside a component or another hook.
import { useLayoutEffect } from 'react';
useLayoutEffect(() => {
// Run side effect
return () => {
// Clean up
};
}, [dependencies]);
The first parameter is the side effect that you want to run and the second parameter is an array of dependencies. If any of the dependencies change the side effect will be re-run.
This hook should be used when you want to run side effects that need to be run synchronously after the DOM has been updated. For example if you want to measure the size of an element after it has been rendered you can use the useLayoutEffect
hook.
function Measure() {
const [height, setHeight] = useState(0);
const measuredRef = useLayoutEffect((node) => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<div ref={measuredRef}>
<h1>Height: {height}</h1>
</div>
);
}
useContext
This useContext
hook is used to read a context value from the nearest provider for the context. The useContext
hook accepts a context object and returns the current context value for that context.
import { useContext } from 'react';
const value = useContext(MyContext);
The MyContext
is the the provider that you want to read the value from. The useContext
hook will return the current context value for that context. If the context value changes then the component will re-render.
To create a provider you can define this on a component.
const MyContext = React.createContext(defaultValue);
Then you can use the MyContext.Provider
to provide the value to the children components.
<MyContext.Provider value={/* some value */}>
<Child />
</MyContext.Provider>
The useContext
hook is useful when you want to avoid passing props through intermediate components. For example if you have a theme that you want to pass to multiple components you can use the useContext
hook to avoid passing the theme prop to all the components.
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
<Footer />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>Theme: {theme}</div>;
}
function Footer() {
const theme = useContext(ThemeContext);
return <div>Theme: {theme}</div>;
}
This can be used at many levels of the component tree to avoid passing props through intermediate components all the way down the tree. Any child component of Toolbar or Footer will be able to access the ThemeContext value.
useReducer
The useReducer
hook is used to manage complex state logic in a component. This hook must be called from inside a component or another hook.
import { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, initialState);
This should be used when you have complex state logic that involves multiple sub-values or when the next state depends on the previous state. For example if you have a counter that you want to increment or decrement you can use the useReducer
hook like the example above.
function Counter() {
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>{state.count}</h1>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
</div>
);
}
The reason why you would use this instead of useState
directly is when the state logic is more complex. For example if you have a form that has multiple fields and you want to update the state based on the field that is being updated you can use the useReducer
hook.
function Form() {
const initialState = { name: '', email: '' };
const reducer = (state, action) => {
switch (action.type) {
case 'CHANGE_NAME':
return { ...state, name: action.payload };
case 'CHANGE_EMAIL':
return { ...state, email: action.payload };
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, initialState);
return (
<form>
<input
type="text"
value={state.name}
onChange={(e) => dispatch({ type: 'CHANGE_NAME', payload: e.target.value })}
/>
<input
type="email"
value={state.email}
onChange={(e) => dispatch({ type: 'CHANGE_EMAIL', payload: e.target.value })}
/>
</form>
);
}
useCallback
useCallback
will let you cache a function so that it doesn't change between renders. This is useful when you want to pass a function to a child component that relies on the function not changing.
const memoizedCallback = useCallback(
() => {doSomething(a, b);},
[a, b]
);
The first parameter is the function you want to memoize and the second parameter is an array of dependencies. If any of the dependencies change the function will be re-created.
This hook should be used when you want to optimize performance by avoiding unnecessary re-renders. For example if you have a component that takes a function as a prop and you don't want it to re-render when the parent component re-renders. This is when you will use the useCallback
hook.
In this example
function ProductPage({ productId, referrer, theme }) {
// ...
return (
<div className={theme}>
<ShippingForm onSubmit={handleSubmit} />
</div>
);
The ShippingForm takes a parameter of handleSubmit
but it is nested inside a div which has a theme parameter, when theme is changed React will re-render the child components and therefore the handleSubmit
function will be re-created. To avoid this you can use the useCallback
hook.
function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback(() => {
// ...
}, [productId, referrer]);
return (
<div className={theme}>
<ShippingForm onSubmit={handleSubmit} />
</div>
);
}
Now the handleSubmit
function will only be re-created when productId
or referrer
change.
The useCallback
hook is similar to the useMemo
hook but it is used for functions instead of values. useMemo
will cache the value of the function and useCallback
will cache the function itself, avoiding the function to be rebuilt. Therefore you can achieve the samething by doing the following.
function useCallback(fn, deps) {
return useMemo(() => fn, deps);
}
If you are writing custom hooks then you can use the useCallback
hook to memoize the function that you are returning.
function useHandleSubmit(productId, referrer) {
return useCallback(() => {
// ...
}, [productId, referrer]);
}
This will ensure that the consumer of the custom hook can optimize their own code when it's needed.
useMemo
The useMemo
hook will let you cache the value of a function so that it doesn't change between renders. This is useful when you want to avoid recalculating the value of a function on every render.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
The first parameter is the function you want to memoize and the second parameter is an array of dependencies. If any of the dependencies change the function will be re-calculated.
For example if you need to run a filter on your data and you don't want to re-run the filter on every render you can use the useMemo
hook.
function ProductList({ products, filter }) {
const filteredProducts = useMemo(() => products.filter(filter), [products, filter]);
return (
<ul>
{filteredProducts.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
useTransition
The useTransition
hook allows you to update the state without blocking the UI from rendering
import { useTransition } from 'react';
const [isPending, startTransition] = useTransition();
The useTransition
hook returns an array with two values. The first value is a boolean that indicates if the transition is pending and the second value is a function that you can use to start the transition.
This hook should be used when you want to update the state without blocking the UI from rendering. For example if you have a list of items that you want to update and you don't want the UI to block while the items are being updated you can use the useTransition
hook.
function List({ items }) {
const [isPending, startTransition] = useTransition();
const [newItems, setNewItems] = useState([]);
const handleClick = () => {
startTransition(() => {
setNewItems([...items, 'new item']);
});
};
return (
<div>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
{isPending && <p>Loading...</p>}
<button onClick={handleClick}>Add Item</button>
</div>
);
}
useDeferredValue
The useDeferredValue
hook is used to defer the value of a resource like a promise. This hook must be called from inside a component or another hook.
import { useDeferredValue } from 'react';
function MessageComponent({ messagePromise }) {
const deferredMessage = useDeferredValue(messagePromise);
return <Message message={deferredMessage} />;
}
This can be used when you need to defer the value to help improve the performance of the application. For example if you have a message that is being fetched from an API and you don't want to show the message until it has been fetched you can use the useDeferredValue
hook.
A good example of using this is inside a <Suspense>
component when you want to fetch a new value, it will mean you can continue to show the old value or the fallback until the new value is ready.
function MessageComponent({ messagePromise }) {
const deferredMessage = useDeferredValue(messagePromise);
return (
<Suspense fallback={<Loading />}>
<Message message={deferredMessage} />
</Suspense>
);
}
useRef
The useRef
hook is used to create a mutable object that persists for the lifetime of the component. This is useful when you want to store a reference to a DOM element or a value that doesn't change between renders.
import { useRef } from 'react';
const ref = useRef(initialValue);
For example if you want to store a reference to a DOM element you can use the useRef
hook.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useImperativeHandle
useImperativeHandle
is a hook that allows you to customize the instance value that is exposed to parent components when using ref
. This is useful when you want to expose a custom API to the parent component.
import { useImperativeHandle, forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return <input ref={inputRef} />;
});
This hook should be used when you want to expose a custom API to the parent component. For example if you have a custom input component that you want to expose a focus method to the parent component you can use the useImperativeHandle
hook.
function App() {
const inputRef = useRef();
return (
<div>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</div>
);
}
useId
useId
is a hook that generates a unique id for a component. This is useful when you want to generate a unique id for a component that can be used in other components.
import { useId } from 'react';
function MyComponent() {
const id = useId();
return <div id={id}>Hello World</div>;
}
useDebugValue
The useDebugValue
will allow you to add a label to the React DevTools for custom hooks. This is useful when you want to provide more information about the custom hook to the developer.
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// Show a label in DevTools next to this Hook
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
}, [friendID]);
return isOnline;
}
useSyncExternalStore
The useSyncExternalStore
hook is used to sync the state of a component with an external store. This hook must be called from inside a component or another hook.
import { useSyncExternalStore } from 'react';
function MessageComponent({ messagePromise }) {
const [message, setMessage] = useState(null);
useSyncExternalStore(messagePromise, setMessage);
return <Message message={message} />;
}
The external store will be something like a promise or a value that is being fetched from an API. For example if you have a message that is being fetched from an API and you want to sync the state of the component with the message you can use the useSyncExternalStore
hook.
useOptimistic
The useOptimistic
hook is used to update the UI optimistically before the server responds. This hook must be called from inside a component or another hook.
import { useOptimistic } from 'react';
function MessageComponent({ messagePromise }) {
const [message, setMessage] = useState(null);
const optimisticMessage = useOptimistic(messagePromise, setMessage);
return <Message message={optimisticMessage} />;
}
This function is useful when you want to update the UI optimistically before the server responds. For example if you have a message that is being sent to the server and you want to show the message before the server responds you can use the useOptimistic
hook.
use
The use
hook used to read a value of a resource like a promise. This hook must be called from inside a component or another hook.
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
}
Custom Hooks
There are a few ways to reuse functionality in React.
- Reuse markup - use a component
- Reuse vanilla JS logic - use a function
- Reuse React logic - use a custom hook
Custom hooks are JavaScript functions that use React hooks. They can be used to share logic between components.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
You can then use the custom hook in your components.
function App() {
const { data, loading } = useFetch('https://api.example.com/data');
if (loading) {
return <div>Loading...</div>;
}
return <div>{data}</div>;
}
Custom hooks can be used to encapsulate logic that is shared between components. They can be used to separate concerns and make your code more modular and reusable.
Top comments (1)
Great inspirational article. Good job!