Custom React Hooks: Why Do We Need a Context
Sometimes a hook is just a hook, but often you'll need more context. This short blog post sums it up.

Custom React Hooks: Why Do We Need a Context
Sometimes a hook is just a hook, but often you'll need more context. This short blog post sums it up.


Custom React Hooks is a very convenient way to encapsulate logic and pass the data down the rendering tree.
The rules for custom React Hooks are quite simple:
A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
Pure HooksLink to this section
Consider this very naive implementation of a custom hook that encapsulates permissions mapping for some shared document service:
<>Copyimport React from 'react'; import { Permissions, usePermissions } from '@hooks/permissions'; const useIsPermitted = () => { // usePermissions is another custom hook that returns an array of permissions const permissions = usePermissions(); return { isEditPermitted: permissions.includes(Permissions.EDIT_SITE_PERMISSION), } }
As you can see this hook is pretty simple - it makes use of another custom hook that returns an array of permissions and maps it into a simple permitted/non-permitted dictionary. This will allow for better code reuse across the application and help us avoid code duplication in every place where we need this check:
<>Copyimport React from 'react'; export const EditButton = () => { const { isEditPermitted } = useIsPermitted(); return <button disabled={!isEditPermitted}>Edit</button> }
The only problem is that this logic will run on every re-render. In case of a small array it's negligible but if it's a large array then we're in trouble. A simple addition to the custom hook can solve this issue:
<>Copyimport React, { useMemo } from 'react'; import { Permissions, usePermissions } from '@hooks/permissions'; const useIsPermitted = () => { // usePermissions is another custom hook that returns an array of permissions const permissions = usePermissions(); return useMemo(() => ({ isEditPermitted: permissions.includes(Permissions.EDIT_SITE_PERMISSION), }), [permissions]); }
This way the permissions will be rematched only when the permissions array changes.
Case solved! Or is it?
Official React documentation can give us a hint what might be wrong with this approach:
Do two components using the same Hook share state? No. Custom Hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), but every time you use a custom Hook, all state and effects inside of it are fully isolated.
How does a custom Hook get isolated state? Each call to a Hook gets isolated state. Because we call useFriendStatus directly, from React’s point of view our component just calls useState and useEffect. And as we learned earlier, we can call useState and useEffect many times in one component, and they will be completely independent.
Effectively this means that if we call useIsPermitted from two different components (or even twice from the same component), the logic will be executed for every instance of the useIsPermitted invocation, even thought we use useMemo inside.
Custom Hooks with ContextLink to this section
A solution for this would be combining a custom hook with a context.
Let's revise our useIsPermitted hook implementation:
<>Copyimport React, { createContext, useMemo } from 'react'; import { Permissions, usePermissions } from '@hooks/permissions'; export const PermissionsContext = createContext({}); export const IsPermittedProvider: React.FC = ({ children }) => { const permissions = usePermissions(); const permissionsDictionary = useMemo(() => ({ isEditPermitted: permissions.includes(Permissions.EDIT_SITE_PERMISSION), }), [permissions]); return ( <PermissionsContext.Provider value={permissionsDictionary} > {children} </PermissionsContext.Provider> ); };
<>Copyimport React, { useContext } from 'react'; import { PermissionsContext } from '@contexts/permissions'; export const useIsPermitted = () => useContext(PermissionsContext);
Now the logic is scoped to a specific context provider. That means that if we use this provider only once at the root of the application, the logic will be executed only once:
<>Copyimport React from 'react'; import { App } from './app'; <PermissionsProvider> <App /> </PermissionsProvider>
Of course we can decide to put the provider lower in the rendering tree so that the logic will be executed only when a relevant part is rendered, but bottom line is - we have more control over the granularity now.
When to Use WhatLink to this section
While a hook with context seems to be a more robust solution in terms of performance and memory consumption, it doesn't mean that you should always go with this approach.
There is place for both approaches, but it's important to understand the implications of each one of them.
Here is a short checklist that will help you decide on the right approach:
Use Pure Hook when:
- Custom hook state must be isolated (different per instance) OR
- No heavy calculations are performed in the custom hook OR
- You only use the hook once in the application (as a way to pass data down the rendering tree)
Use Hook with Context when:
- The whole subtree must share the hook's state OR
- There is a heavy calculation performed inside the hook and you want it to run as seldom as possible
Follow me if you liked the article, comment here or DM on Twitter if you have any questions.
Comments (3)
The code below "Let's revise our useIsPermitted hook implementation:" is missing. Thanks for your article.
Thanks for letting know, should be OK now.
thanks for article, you can use constate for create hook with context and this lib has memo on selectors
Thanks for mentioning that, nice lib! Could you elaborate on what's the advantage of using this lib VS plain state and context? As I see it the only advantage is syntactic sugar and as for disadvantages there are two:
- Tight coupling of state to the hosting component (without this lib you can encapsulate the state inside the provider)
- Another 0.5k to the bundle size (negligible but feels unnecessary)
About the author

Fullstack engineer and guild master @ Wix.com, author of @angular-builders and jest-marbles, drummer and kitesurfer.

About the author
JeB
Fullstack engineer and guild master @ Wix.com, author of @angular-builders and jest-marbles, drummer and kitesurfer.
About the author

Fullstack engineer and guild master @ Wix.com, author of @angular-builders and jest-marbles, drummer and kitesurfer.