Introduction to React Hooks

Danielle Fenske
PayScale Tech
Published in
6 min readJul 19, 2019

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.

View in Codepen

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.

View in Codepen

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:

  1. The first is the function that will be run when the effect is triggered
  2. 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.

View in Codepen

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.

View in Codepen

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 refs.

Class Component (37 lines)

We used to be limited to only using refs 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.

View in Codepen

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.

View in Codepen

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).

View in Codepen

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.

View in Codepen

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 for memoize — 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 to useMemo, 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 multiple useState and useEffect 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 one useState declared. It gets messy.
  • Same goes for useEffect — use one useEffect statement per side effect you are controlling.
  • You can write your own hooks too!

Resources

--

--

Danielle Fenske
PayScale Tech

I am a Software Engineer at PayScale, a crowd-sourced compensation software company. I focus on creating attractive and reliable front-end web apps.