# Custom React Hooks: Why Do We Need a Context

Published 2021-08-16 · https://www.justjeb.com/post/custom-react-hooks-why-do-we-need-a-context

Why and when to use pure hooks VS hooks with context

[TL;DR](https://www.justjeb.com/post/custom-react-hooks-why-do-we-need-a-context#when-to-use-what)

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](https://legacy.reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook):

> **A custom Hook is a JavaScript function whose name starts with “use” and that may call other Hooks.**

## Pure Hooks

Consider this very naive implementation of a custom hook that encapsulates permissions mapping for some shared document service:

```tsx
import 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_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:

```tsx
import 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:

```tsx
import 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_PERMISSION),
  }), [permissions]);
}
```

This way the permissions will be rematched only when the **permissions** array changes.

Case solved! Or is it?

Official React [documentation](https://legacy.reactjs.org/docs/hooks-custom.html#using-a-custom-hook) 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](https://legacy.reactjs.org/docs/hooks-state.html#tip-using-multiple-state-variables) [earlier](https://legacy.reactjs.org/docs/hooks-effect.html#tip-use-multiple-effects-to-separate-concerns), 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 though we use **useMemo** inside.

## Custom Hooks with Context

A solution for this would be combining a custom hook with a context.

Let's revise our **useIsPermitted** hook implementation:

```tsx
import 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:

```tsx
import React from 'react';
import { App } from './app';
import { PermissionsProvider } from '@contexts/permissions';

<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 What

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
