Introduction to React Hooks
Why do we need Hooks?
How many times have you written a simple functional React component, only to realize you need to store state, and therefore you must convert it to a class component? You have to change the component declaration, add a constructor, and use the lifecycle methods.
React Hooks provide you the option of leaving the component functional, while still being able to keep track of state. They are very powerful, but require a bit of a paradigm shift to get used to. In this blog post, I will show examples of components that use React Hooks, and compare them with an equivalent component built using the “old way”. Hopefully this will help you understand why React Hooks are useful and allow you to start using them in your own code.
Hook #1: useState
useState
allows you to add state to your functional components. The syntax is a little funky, but once you know what each parameter stands for, it is easy to pick up.
A simple example:
const [value, setValue] = useState(null);
Which means:
const [<variable name>, <name of handler for that variable>] = useState(<initial value>);
Class Component (23 lines)
Here, we have a very simple example. It displays an input, and below the input, displays “Hello” + the value of the input. Every time we type in the input, the handleChange
function is called which updates state, and the whole component is re-rendered.
Functional Component with React Hooks (10 lines)
In the React Hooks version of the component, we see that we define our state value (name
), the handler (setName
), and the initial value for name (‘’
), all on one line. We can then reference these in the component below.
Hook #2: useEffect
useEffect
takes the place of some lifecycle methods on class components; the React team describes it as a combination between componentDidMount
, componentDidUpdate
, and componentWillUnmount
. We can utilize useEffect
to trigger side effects.
useEffect
takes two parameters:
- The first is the function that will be run when the effect is triggered
- The second is an optional list of dependencies. The effect will only run if one of the dependencies in the list changes. If the list of dependencies is left empty (
[]
), then the effect will only be run once, when the component mounts.
A simple example:
useEffect(async () => {
// Fetch some data when name changes
const result = await fetch(`/getPerson/${name}`); // Do something with the result
setPerson(result);
}, [name]);
Or in generic terms:
useEffect(<function to call>, [<list of dependencies>]);
When trying to think of useEffect
in relation to the lifecycle methods, you can consider it this way (this list came from the slides associated with this hooks tutorial).
- Mount: Run effect
- Update: Clean effect, Run effect
- Unmount: Clean effect
The clean effect stage is where you can do some clean up if you need to — eg. if you added an eventListener
with the effect, in the cleanup, you would want to remove that eventListener
.
The way to add a clean-up stage in the useEffect
syntax is by adding a return statement that returns a function. (The following code snippet came from the slides from this React hooks tutorial).
function ClickSpy() {
useEffect(() => {
function handleClick() {
alert('clicked')
}
document.addEventListener('click', handleClick)
// The function return by the effect is the clean up
return () => {
document.removeEventListener('click', handleClick)
}
}) return null
}
Class Component (38 lines)
This class uses componentDidMount
to fetch some data from Reddit. The response is then rendered once the fetch has returned.
Functional Component with React Hooks (28 lines)
In the functional version, we use useState
to store loading
and redditResult
, and we use useEffect
with no dependencies to run the fetch
on the first mount of the component.
Hook #3: useRef
Although it’s not typically recommended, sometimes you need to access the DOM directly in React. One good example is modals — to open and close them it is helpful to just be able to access the DOM to manipulate them. To do this, you need to use ref
s.
Class Component (37 lines)
We used to be limited to only using ref
s in class components. The way to use them was to create one using createRef()
, assign it as a field on the component instance, and then refer to it throughout the component this way: this.imgRef
. In this sample, we have a button that, when clicked, will randomly choose one of the four kitten images and replace the image tag’s src
attribute with the new URL.
Functional Component with React Hooks (30 lines)
With the new useRef
hook, we can have a ref
inside of a functional component. We create a reference first with useRef(defaultValue)
, use that reference in the <img>
tag, and then we can manipulate that DOM element using the imgRef
we created.
Hook #4: useContext
The Context API is a tool that was introduced in React v16.3. It is useful for setting global settings for an app, like a theme or a configuration value. It is an easy way to allow all components to access a value, without having to pass it as a prop through each level of the component hierarchy.
React Hooks added to this useful tool by creating the useContext
hook.
Old Way: Providers and Consumers (26 lines)
To use the Context API, you must create a context with React.createContext(defaultValue)
. This instantiates a context, with a default value. In the old way, to make the value of Context available to components, you needed to use Providers and Consumers to wrap React components. In the parent component, you would wrap the child in a <Provider>
component, and then in the child component, you wrap a component in the <Consumer>
to make the value of Context available. To use the value, you use the render props
format.
In the example below, we want to use the Context
to store the color theme for our component, using the same input and text components that we saw in our first example. If we render the <App>
component, we explicitly set the context’s value to ‘red’, so the colors will be red. However, if we just render <MyComponent>
to the page, we have not passed in a value to the context, so it will use the default, ‘blue’ (try this out in Codepen using the link below the snippet and see for yourself).
React Hooks (22 lines)
With hooks, you will still instantiate the context with React.createContext()
. The difference here is that instead of having to wrap your child component with the Consumer
to make the context’s value available, we can just use React.useContext(<contextName>)
to get the value.
Conclusion
We have covered 4 of the basic React hooks: useState
, useEffect
, useRef
, and useContext
. Some other hooks we did not cover are useReducer
, useCallback
, and useMemo
. These hooks are more complex — a basic description of what each one does is:
useReducer
: a more complex version of setState, which lets you configure multiple different action types for different scenarios.useMemo
: this stands formemoize
— a way of caching results or values, so that if you input the same parameters into a function twice, instead of redo-ing the calculation the second time, the function will just returned the result from the first time.useCallback
: Similar touseMemo
, but used for memoizing a callback function instead of a value.
Some other things to note before you go out into the world and start using React Hooks everywhere
- Hooks must be called in the same order every time — you cannot use them inside
if
statements or conditionals. The React documentation explains: “That’s what allows React to correctly preserve the state of Hooks between multipleuseState
anduseEffect
calls”. There is an eslint-plugin to help you make sure you’re sticking to this rule. - State is not merged when using
useState
. The new state overwrites the old state — this is important if you have an object in state and you’re just trying to set one property. You need to do the merge yourself. - It is recommended to use one
useState
per piece of state stored. Don’t try to store all state in one object with oneuseState
declared. It gets messy. - Same goes for
useEffect
— use oneuseEffect
statement per side effect you are controlling. - You can write your own hooks too!
Resources
- My CodePen Collection of all the above code snippets: https://codepen.io/collection/nwqdpg/
- Hooks at a Glance: https://reactjs.org/docs/hooks-overview.html
- Hooks API Reference: https://reactjs.org/docs/hooks-reference.html
- Great React Hooks Tutorial: https://code.react-advanced.smooth-code.com
- Github repository for a great Pluralsight course on React Hooks: https://github.com/pkellner/pluralsight-course-using-react-hooks
- eslint-plugin-react-hooks