React Hooks: Recipes
An article providing concrete examples of why and how one uses most of the React Hooks.

Organization
This article is organized into sections exploring each of the React Hooks. The section starts with a classic (pre-Hooks) React example component followed by a React Hooks component example implementing the same features. The sections include:
- Callback
- State
- Effect
- Sidebar into the Second Parameter
- Context
- Ref
- Memo
- Others
The examples are available for download.
Callback
Handling events, e.g., DOM events, is a core React feature. The (most recent) classic approach to handling events is to use the public class field syntax; demonstrated in src/components/Callback.jsx:
Observations:
- The handleClick function is bound to the component, i.e., one can use this in the function to refer to the component
- The onClick property is provided the same function between renders; this is important as one does not want to inadvertently cause a child component to re-render because a callback reference is modified
With React Hooks, handling events is accomplished using the useCallback hook; demonstrated in src/components/CallbackHook.jsx:
Observations:
- Unlike class-based components, the reference to this is undefined
- Much like the classic approach, the onClick property is provided the same function between renders; the useCallback hook memoizes the supplied function (it caches it)
- In a later section, we will explore the purpose of the second parameter of the useCallback call, in this case an empty array
State
React components can classically maintain their own state using the component’s state property; demonstrated in src/components/State.jsx:
Observation:
- state is object; in this case with properties value and value2 storing the values of the two counters
- Here we initialize the state using the public class field syntax; this avoids having to define a constructor function (an old school approach)
- The component’s setState method is used to update state and triggering a re-render
- When setting state based on the current state properties, e.g., incrementing, we are required to use the functional form of setState; otherwise we can simply set the state value directly. In either case, we simply provide the updated portion of the state and React merges it into state
note: This last requirement is subtle; the setState function is asynchronous, i.e., the actual setting of state happens in the future where the state might have already been changed
With React Hooks, maintaining state is accomplished using the useState hook; demonstrated in src/components/StateHook.jsx:
Observations:
- Unlike classic React, components one typically uses multiple calls to useState to manage portions of the component’s state as separate objects, e.g., value and value2
- Like the setState method, we are required to use the functional form when setting a state value is based on the current state value
- The returned setter functions, e.g., setValue, is guaranteed to be the same function between renders
Effect
React provides various mechanisms for one to implement side-effects to rendering. In classic React, this is accomplished using lifecycle methods, e.g., componentDidMount.
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree). Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
— React — React Component
Demonstrated in this simple example that fetches and displays posts; src/components/Effect.jsx:
We can use the useEffect hook to accomplish the same thing; src/components/EffectHook.jsx:
Observations:
- As the function provided to useEffect itself cannot be asynchronous, we simply define an asynchronous function (execute) inside it and then execute it
- As in the useCallback hook, we supply an empty array as the second parameter to the call to useEffect. In this case, passing an empty array is critical to the functionality; causes the supplied function to be only executed once (after the component first renders).
Sidebar into The Second Parameter
Many of the React hooks accept a second parameter, e.g., useCallback and useEffect; so far we have been supplying empty arrays. We have come to learn that by supplying an empty array the hook “does its thing” only once.
In the case of useCallback, the provided function is memoized (cached) once when useCallback is first called.
In the case of useEffect, the provided function is only executed once when useEffect is first called.
We can explore true purpose of this second parameter through exploring another React lifecycle method, componentDidMount:
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
— React — React Component
In this example, the component accepts an id property; uses it to fetch and display a single post; src/components/Effect2Post.jsx:
Observations:
- Because componentDidUpdate does not execute on the first render, we continue to use componentDidMount
- An important behavior in componentDidUpdate is that the update operation (effect) only happens when the id property changes. Conditions like this are required to prevent infinite render loops
We can use the useEffect hook (and a second parameter) to accomplish the same thing; src/components/EffectHook2Post.jsx:
Observations:
- Here we can start to see how React Hooks starts to simplify the code; 39 vs 61 lines in the previous classic React example
- Instead of having to use two methods (componentDidMount and componentDidUpdate), we use a single function (useEffect)
- The logic of comparing the current and previous values of the id property is declaratively provided by passing the array, [id], as a second parameter
This second parameter, often referred to as the array of dependencies, is used to determine under what conditions a Hook “does it thing”; specifically after the first execution, executing if any of the values in the array change. This explains why using an empty array causes only a single execution.
note: Not supplying the second parameter has the effect of having the Hook “do its thing” after each render.
Context
React Context is a relatively new (in last year or two) feature:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
— React — Context
Following the theme example, we use the ThemeContext in a deeply nested component. The classic approach to this is to use a Render Prop component to access the value; demonstrated in src/components/Context/ContextMiddle/ContextMiddleUse.jsx:
note: While this is fairly straightforward with a single context, it gets messy quick when trying to access multiple contexts; having to nest multiple Render Prop components.
We can use the useContext hook to accomplish the same thing (does not suffer the nesting problems with multiple contexts); src/components/ContextHook/ContextHookMiddle/ContextHookMiddleUse.jsx:
Ref
In classic React, there is an escape hatch to imperatively interact with children:
In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch.
When to Use Refs
There are a few good use cases for refs:Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
— React — Refs and the DOM
A common example of this is interacting with the video element. Here is an example, were we can trigger a video to play from React; src/components/Ref.jsx:
We re-implement this with the useRef Hook; src /components/RefHook.jsx:
Observations:
- It important to note myRef variable will always refer to the same object (so we don’t need to add it the array of dependencies for handleClick)
Ref (A Subtle Example)
In the previous example, we used the useRef Hook to re-implement the classic React example of using createRef; it turns out that there other reasons to use it.
However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.
This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: …} object yourself is that useRef will give you the same ref object on every render.
— React — Hooks API Reference
In this, somewhat contrived, classic React example we illustrate where having instance fields (in this case the state itself) is important to the implementation. This example sets up an interval that console logs the component’s state’s counter value each second; src/components/Ref2.jsx:
note: This example is inspired by an article a colleague of mine recently wrote; React’s useEffect and useRef Explained for Mortals.
Observations:
- The key to this implementation is that handleInterval function has access to the current value of the state property as it exists as an instance field
In order to implement this same feature with React Hooks, we need to be a bit creative; src/components/Ref2Hook.jsx:
Observations:
- The challenge here is that without class properties, we need an alternate mechanism for the function passed to setInterval after the first render to access an updated value reflecting the counter state
- The solution, in this case, is to maintain a second value mirroring counter with myRef; this is a bit fragile, but could not determine a better solution
BTW, this example is a particular example of a more general challenge with React Hooks; stale closures.
So far it seems like the biggest confusion point with Hooks is stale closures. It’s also the one missing in the docs. In hindsight we should have documented that in detail. But with a new paradigm it’s a bit difficult to know what ends up an issue in practice.
— Dan Abramov — Twitter Post
Memo
In this example component, we store a list of things as an array of their ids (as ids) and an object with ids as keys and things as values (as byId).
note: This pattern makes it particularly efficient to access individual things; commonly used in Redux.
In order to obtain an array of things to pass to a child component, one conceptually maps ids to things in the render method. The problem, however, with this approach is that this mapping would create a new array each time it is run; this means that the child component (even if optimized to only render on property changes) would always render when the parent renders.
One way to prevent the child component from unnecessarily rendering, is to use the memoize-one library to create a function that memoizes (caches) the array of things.
All of this is demonstrated in src/components/Memo.jsx:
Observations:
- In this example, the MemoThings child component is optimized to only render when its properties change; checkout React.memo for how this was done
- When value in state is incremented, things is set to a cached value; the MemoThings component does not unnecessarily render
- However, when byId or ids in state are changed, things is re-calculated; the MemoThings component does appropriately render
This same functionality can be implemented using the useMemo Hook; src/components/MemoHook.jsx:
Others
At the point in time, there are three additional React Hooks that we did not cover:
- useImperativeHandle
- useLayoutEffect
- useDebugValue
As this article is already too long and these Hooks are pretty specialized (and unlikely to be used in most projects), will leave it to reader to explore them.