State Scope with Providers - Next.js with Jotai
Tags: nextjs, jotai, web3, typescript
Published: 2024-04-19 05:31:24

In the  React state management, Jotai emerges as a minimalist and modern library, offering an elegant solution to handle atom-based state with simplicity and precision. This article delves into the practical use of Jotai to create isolated state scopes within a React application, utilizing the concept of Providers. We'll explore how to structure components for shared or independent state management, walking through a hands-on example that illustrates the power and flexibility of Jotai in a React project.

##  Isolating Component States with Jotai Providers
When building complex applications in React, it's common to encounter scenarios where components need to share state or maintain their own independent state. Jotai, a state management library, provides a straightforward and efficient way to handle these requirements. Let's look at a practical example to understand how Jotai achieves this with its Provider pattern.

In the code snippet below, we have a `SharedComponent` that is utilized multiple times within two separate contexts created by `Provider`.

```typescript
// page.tsx
import Provider from './path-to-provider'; // Ensure to import the Provider from its file
import SharedComponent from './shared-component'; // Ensure to import SharedComponent

const Page = () => {
  return (
    <div>
      <Provider>
        <SharedComponent /> {/* C1 */}
        <SharedComponent /> {/* C2 */}
      </Provider>
      <Provider>
        <SharedComponent /> {/* C3 */}
        <SharedComponent /> {/* C4 */}
      </Provider>
    </div>
  );
};

export default Page;
```

Each `Provider` in the code above creates a unique context for state management. Consequently, `SharedComponent` instances within the same `Provider` share a common state, while those under different `Provider` instances maintain their own state, isolated from others.

Here's how `SharedComponent` connects to the state using Jotai:

```typescript
// shared-component.tsx
import { atom, useAtom } from 'jotai';

const countAtom = atom(0); // This atom holds the state for our component

const SharedComponent = () => {
  const [count, setCount] = useAtom(countAtom); // This hook connects our component to the atom's state

  return (
    <div>
      Shared component
      <div>{count}</div>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>
        +1
      </button>
    </div>
  );
};

export default SharedComponent;
```
Clicking the "+1" button in any instance of `SharedComponent` (C1, C2, C3, C4) will result in:

- Simultaneous increment of count in C1 and C2, as they share the same state within a single `Provider`.
- Simultaneous increment of count in C3 and C4 for the same reason.
- Independent state changes between C1/C2 and C3/C4 due to the distinct `Provider` wrapping, creating isolated states.

This example demonstrates the use of `Provider` to create separate "contexts" or "scopes" of state within a React application, facilitating both shared and independent state management across the component hierarchy.

##  Structuring Shared and Isolated States with Jotai
In this section, we'll further explore how to share or isolate state between components in a React application using Jotai atoms and Providers.

We begin by declaring a shared atom in a separate file:

```typescript
// atoms.ts
"use client";
import { atom } from 'jotai';

export const sharedCountAtom = atom<number>(0);
```

By exporting the `sharedCountAtom`, we make it available to any component that needs to tap into this shared state.

The main page's component structure is as follows:

```typescript
// page.tsx
import { Provider } from 'jotai';
import ComponentC from './ComponentC';
import ComponentD from './ComponentD';

const Page = () => {
  return (
    <div>
      <Provider>
        <ComponentC /> {/* Component C1 */}
        <ComponentD /> {/* Component D1 */}
      </Provider>
      <ComponentC /> {/* Component C2 */}
      <ComponentD /> {/* Component D2 */}
    </div>
  );
};

export default Page;
```

`ComponentC` and `ComponentD` are functionally identical and use the `sharedCountAtom` for state management. The context in which they are rendered, inside or outside a `Provider`, determines their state sharing behavior:

- `ComponentC1` and `ComponentD1` inside a `Provider` share the same atom state.
- `ComponentC2` and `ComponentD2` outside a `Provider` share a different instance of the atom state, isolated from `ComponentC1` and `ComponentD1`.

Here are the component implementations:

```typescript
// ComponentC.tsx
"use client";
import { useAtom } from 'jotai';
import { sharedCountAtom } from './atoms';

const ComponentC = () => {
  const [count, setCount] = useAtom(sharedCountAtom);
  return (
    <div className='border-2 w-60 h-60'>
      Component C
      <div>Count: {count}</div>
      <button
        className="border-1 border-black bg-blue-300 text-white hover:bg-yellow-200 p-3"
        onClick={() => setCount((prev) => prev + 1)}
      >
        +1
      </button>
    </div>
  );
};

export default ComponentC;
```

```typescript
// ComponentD.tsx
"use client";
import { useAtom } from 'jotai';
import { sharedCountAtom } from './atoms';

const ComponentD = () => {
  const [count, setCount] = useAtom(sharedCountAtom);
  return (
    <div className='border-2 w-60 h-60'>
      Component D
      <div>Count: {count}</div>
      <button
        className="border-1 border-black bg-blue-300 text-white hover:bg-yellow-200 p-3"
        onClick={() => setCount((prev) => prev + 1)}
      >
        +1
      </button>
    </div>
  );
};

export default ComponentD;
```

Note: It is crucial to include the directive "use client" in all components to ensure proper client-side state management, preventing potential issues.

In summary, `ComponentC` and `ComponentD` may import the same atom from `atoms.ts`, but their state sharing is controlled by the use of `Provider`. This setup exhibits the adaptability of Jotai in managing state across various scopes within a React application.

## Conclusion
The React ecosystem offers various approaches to state management, each with its own set of trade-offs. Jotai, with its minimalist and atom-based approach, provides a powerful yet simple solution for developers to control state scope within their applications. By leveraging Providers, developers can easily create shared or isolated state contexts, ensuring that components behave predictably and maintain clean separation of concerns. As we've seen in the examples above, Jotai's flexibility and ease of use make it an excellent choice for React developers looking to streamline their state management practices.

## Reference:
The first example is inspired from this article - [# Jotai V2 Notes 02 — Store & Provider](https://arc.net/l/quote/nivugygy)