React Hooks is the new hotness in the React world. I'm writing steadily more and more of them and I thought it would be useful to have a cheatsheet to refer back to which encompasses the basic hooks as well as the intricacies of useEffect
. Check out the official Hooks API Reference for more in-depth information.
Table of Contents
- useEffect for lifecycle methods
- useEffect for general side effects
- useState
- useReducer
- Building Your Own Hooks
useEffect (for lifecycle methods)
useEffect
, among other things, allows you to write your own side effects and trigger a re-render when needed.
But to make it simpler, useEffect also substitutes lifecycle methods. Let's talk about them.
substitute for componentDidUpdate + componentDidMount
When does it run? On every render
What's the catch? It's not just a componentDidUpdate
replacement, it also runs on mount. So it's not 1-to-1
Important features? useEffect can take in a 2nd argument, you have to skip that argument. You can also return a function, we'll cover that in the next section.
Code sandbox playground: Go play with it
Syntax:
import { useEffect } from 'react';
useEffect(() => {
// whatever runs here will run on each re-render
});
substitute for componentDidMount + componentWillUnmount
When does it run? On component mount and unmount
What's the catch? The syntax is very close to the previous use case. It threw me off several times but it makes sense once you read the docs. If the effect runs more than once, make sure you passed in the 2nd argument
Important features? This is an effect that runs only once. The mount logic goes in the body of the effect function, the unmount/cleanup logic goes into a function that you return from the effect.
Code sandbox playground: Go play with it
Syntax:
import { useEffect } from 'react';
useEffect(() => {
// run mount logic here such as fetching some data
return () => {
// unmount logic goes here
};
}, []); // note the empty array
You can leave either the mount
or unmount
logic empty to work only off one of those lifecycle substitute. Meaning that:
- leave
mount
logic empty so that onlyunmount
logic runs (substitute for justcomponentWillUnmount
) - return nothing so that only
mount
logic runs (substitute for justcomponentDidMount
)
useEffect for side effects
useEffect
's primary goal is to encompass any side effect you might want to use. A side effect is essentially something that you do within your component which affects the world at large. Whether that's a network request, setting the document title, or what have you.
Run when necessary
When does it run? when the component re-renders, useEffect
will check dependencies. If the dependency values changed, useEffect will run the effect
What's the catch? React does a shallow comparison. If you use an object or an array that you mutate, React will think nothing changed.
Important features useEffect skips running the effect when things don't change. You don't actually have to use the dependency values in the effect. You can pass in a prop value as a dependency.
Code sandbox playground: Go play with it
Syntax:
import { useEffect } from 'react';
function SomeComponent(props) {
useEffect(() => {
// logic runs only when dependency variables changed
}, [arrOfDependency, values, props.id]); // array of values to check if they've changed
}
Potential use cases
Since the hook is more difficult to explain, I'd like to offer a list of use cases
- run a side effect (like a fetch) when a prop changes to get new data
- run a resource-heavy calculation only when the calculation values change
- update the page (like document title) when a value updates
useState
State is probably the reason why people switch from stateless (functional) components to class components. useState
allows us to have stateful components without classes.
What does it return? Current state and a function that lets you set state
What's the catch? The state setting function will replace the previous state with the new one rather than merging them as class state would have. You need to merge your objects yourself before setting the state.
Important features You can use as many useState
hooks in your component as you want. Passing any value to useState
will create the initial state. It's also a convention to not call the variables state
and setState
but rather by contextual names (eg. user
and setUser
). useState
accepts any value for state, it doesn't have to be an object.
Code Sandbox playground: Check out the useState examples
Syntax:
import { useState } from 'react';
// setup
const defaultValue = { name: "Antonin" };
const [state, setState] = useState(defaultValue);
// scenario 1 usage
// resulting state only contains key `user` with value 'antjanus'
setState({ user: 'antjanus' });
// scenario 2 usage
// resulting state contains key `name` with value 'A. Januska'
setState({ name: 'A. Januska' });
// scenario 3 usage
// resulting state is a merger between `{ name: 'A. Januska' }` and `{ user: 'antjanus'}`
setState({ ...state, user: 'antjanus'});
useReducer
useReducer
is an alternative to useState
and if you've used Redux in the past, this will look familiar.
What are the arguments? What does it return? useReducer
takes in a reducer
function and the initialState
. It returns the current state
and a dispatcher
(sound familiar?)
How does it run? On state change, dispatch
an object with a type and a data payload (read about flux standard action for more info). The reducer
we passed into useReducer will receive the current state and the dispatched object. It returns the new state.
What's the catch? It's a more complicated workflow but it works just like you'd expect if you've used Redux.
Important features The reducer gets run on every dispatch. It gets access to the previous state. useReducer
also includes a 3rd argument you can use to create the initial state
Code Sandbox playground: Check out the useReducer example
Syntax
import { useReducer } from 'react';
function reducer(currentState, action) {
switch(action.type) {
// handle each action type and how it affects the current state here
}
}
function SomeComponent() {
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({ type: 'ADD', payload: data }); // { type: 'ADD', payload: data } gets passed into the `reducer` as the `action` argument while `state` gets passed in as the `currentState` argument
}
Building Your Own Hooks
A quick note on building your own hooks. It's as easy as using the existing hooks and composing them together inside of a function that starts with use
. Here's a quick example of a useUser
hook.
What are the requirements? That the function starts with the keyword use
. Eg. useUser
or useSomethingElse
.
Important features: you can call any hooks within your custom hook and it works as expected.
Code Sandbox playground: Check out the custom hooks example
Syntax:
import { useEffect } from 'react';
function useUser(userId) {
let [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/user/${userId}`)
.then(data => data.toJSON())
.then(data => setUser(data));
}, [userId]);
return user;
}
function SomeComponent(props) {
const user = useUser(props.id);
}
What about the rest?
There are other hooks you can use such as useMemo
, useCallback
and so on. I would say that those are more advanced hooks and if you understand the basic hooks, go ahead and check out the official docs.
I also understand there are some advanced usage examples for many of these (like passing useReducer's dispatch
down several levels).
If you find something incorrect or some extra information useful that isn't included, let me know! And I'll include it!
Did you find the cheatsheet useful? Buy me a coffee so I can keep doing this and produce more content! :) You can also follow me on Twitter
Top comments (14)
Thanks man. Are you sure about naming with ‘use’?
I don’t think it’s mandatory, you can create any named function as hooks. Probably this might be the recommended way same as how HOC are made with ‘with’ prefix
The React team provide an ESLint plugin that prevents incorrect usage of hooks, but you must name them with
use
.reactjs.org/docs/hooks-faq.html#wh...
Yeah I got it, thanks for the link
It's a suggestion by the React team - a best practice to make the intent clear.
Interesting. I guess I haven't tested it but it's straight from their docs:
Hi Antonin, Thanks for writing this. I think it's very useful.
I have one comment regarding your useUser hook. I think it's important to "cancel" the request before the component unmounts to avoid memory leaks.
I wrote about it a few days ago. You might want to have a look:
dev.to/juliang/cancelling-a-promis...
Cheers!
In this case there's no memory leak though.
It's simply a warning as updating the state will effectively do nothing after the component has been unmounted.
Ah. I see your point. I suppose the dangling reference to the component gets garbage-collected after the promise resolves and state is updated?
Is it possible to use a custom hook in place of something like a render prop or a higher order component?
Yes, that was one of the use-cases they had in mind when creating hooks.
Awesome. I now I need to practice and figure out how to do that.
Nice article, I like the
useReducer
code sandbox example, pretty straight forward. I follow a similar template in my "Guide to Learning React Hooks article", you should check it out.Thanks for an article.
Another great place to wrap a head around hooks is Dan's blog.
Dan's blog is awesome.