Advanced State Management in React with Jotai (TypeScript)

This guide covers:

Basic Atoms
Dependent Atoms
Async Atoms with loadable
Scoped Providers
Accessing Jotai Atoms Outside Components

Prerequisites

You’ll need:

TypeScript set up in your React project.
Install Jotai with npm install jo…


This content originally appeared on DEV Community and was authored by pgangwani

This guide covers:

  • Basic Atoms
  • Dependent Atoms
  • Async Atoms with loadable
  • Scoped Providers
  • Accessing Jotai Atoms Outside Components

Prerequisites

You’ll need:

  • TypeScript set up in your React project.
  • Install Jotai with npm install jotai jotai/utils.

Setting Up

Organize the project with a scalable folder structure.

src/
│
├── atoms/
│   ├── counterAtom.ts        # Basic counter atom
│   ├── dependentAtom.ts      # Dependent atom example
│   ├── dropdownAtoms.ts      # Async atoms with loadable
│   ├── scopedCounterAtom.ts  # Scoped counter atom
│
├── components/
│   ├── Counter.tsx           # Component for basic atom
│   ├── DependentCounter.tsx  # Component for dependent atom
│   ├── AsyncDropdown.tsx     # Component for async dropdowns
│   ├── ScopedCounter.tsx     # Component for scoped counters
│
└── App.tsx                   # Main app file

Basic Atom (Primitive Atom)

Step 1: Define the Atom in TypeScript

// src/atoms/counterAtom.ts
import { atom } from 'jotai';

export const counterAtom = atom<number>(0);

Step 2: Create the Counter Component

// src/components/Counter.tsx
import React from 'react';
import { useAtom } from 'jotai';
import { counterAtom } from '../atoms/counterAtom';

const Counter: React.FC = () => {
  const [count, setCount] = useAtom(counterAtom);

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

Step 3: Use the Counter Component in App.tsx

// src/App.tsx
import React from 'react';
import Counter from './components/Counter';

const App: React.FC = () => {
  return (
    <div>
      <h1>Jotai Basics</h1>
      <Counter />
    </div>
  );
};

export default App;

Creating Dependent Atoms

Step 1: Define Dependent Atom (dependentAtom.ts)

// src/atoms/dependentAtom.ts
import { atom } from 'jotai';
import { counterAtom } from './counterAtom';

export const doubleCounterAtom = atom((get) => get(counterAtom) * 2);

Step 2: Create Dependent Counter Component

// src/components/DependentCounter.tsx
import React from 'react';
import { useAtom } from 'jotai';
import { doubleCounterAtom } from '../atoms/dependentAtom';

const DependentCounter: React.FC = () => {
  const [doubleCount] = useAtom(doubleCounterAtom);

  return (
    <div>
      <h2>Double Count: {doubleCount}</h2>
    </div>
  );
};

export default DependentCounter;

Step 3: Add to App.tsx

// src/App.tsx
import React from 'react';
import Counter from './components/Counter';
import DependentCounter from './components/DependentCounter';

const App: React.FC = () => {
  return (
    <div>
      <h1>Jotai Basics</h1>
      <Counter />
      <DependentCounter />
    </div>
  );
};

export default App;

Async Atom with loadable for Cascaded Dropdowns

Using loadable, we can manage async atoms without needing Suspense, which is ideal for showing loading states directly within the component.

Step 1: Define Async Atoms with loadable (dropdownAtoms.ts)

// src/atoms/dropdownAtoms.ts
import { atom } from 'jotai';
import { loadable } from 'jotai/utils';

export const countryAtom = atom<string | null>(null);

export const stateAtom = loadable(
  atom(async (get) => {
    const selectedCountry = get(countryAtom);
    if (!selectedCountry) return [];

    const response = await fetch(`/api/states?country=${selectedCountry}`);
    return response.json();
  })
);

export const cityAtom = loadable(
  atom(async (get) => {
    const selectedState = get(stateAtom)?.data;
    if (!selectedState) return [];

    const response = await fetch(`/api/cities?state=${selectedState}`);
    return response.json();
  })
);

Step 2: Create the Async Dropdown Component

// src/components/AsyncDropdown.tsx
import React from 'react';
import { useAtom } from 'jotai';
import { countryAtom, stateAtom, cityAtom } from '../atoms/dropdownAtoms';

const AsyncDropdown: React.FC = () => {
  const [country, setCountry] = useAtom(countryAtom);
  const [states] = useAtom(stateAtom);
  const [cities] = useAtom(cityAtom);

  return (
    <div>
      <h2>Cascaded Dropdowns</h2>
      <label>
        Country:
        <select onChange={(e) => setCountry(e.target.value)}>
          <option value="">Select Country</option>
          <option value="usa">USA</option>
          <option value="canada">Canada</option>
        </select>
      </label>

      <label>
        State:
        {states.state === 'loading' ? (
          <p>Loading states...</p>
        ) : (
          <select>
            <option value="">Select State</option>
            {states.data.map((state) => (
              <option key={state.id} value={state.name}>
                {state.name}
              </option>
            ))}
          </select>
        )}
      </label>

      <label>
        City:
        {cities.state === 'loading' ? (
          <p>Loading cities...</p>
        ) : (
          <select>
            <option value="">Select City</option>
            {cities.data.map((city) => (
              <option key={city.id} value={city.name}>
                {city.name}
              </option>
            ))}
          </select>
        )}
      </label>
    </div>
  );
};

export default AsyncDropdown;

Step 3: Add Async Dropdown to App.tsx

// src/App.tsx
import React from 'react';
import AsyncDropdown from './components/AsyncDropdown';

const App: React.FC = () => {
  return (
    <div>
      <h1>Async with Jotai</h1>
      <AsyncDropdown />
    </div>
  );
};

export default App;

Using Scoped Providers

Scoped providers are helpful for creating isolated instances of atoms.

Step 1: Define Scoped Atom (scopedCounterAtom.ts)

// src/atoms/scopedCounterAtom.ts
import { atom } from 'jotai';

export const scopedCounterAtom = atom<number>(0);

Step 2: Scoped Component (ScopedCounter.tsx)

// src/components/ScopedCounter.tsx
import React from 'react';
import { useAtom } from 'jotai';
import { scopedCounterAtom } from '../atoms/scopedCounterAtom';

const ScopedCounter: React.FC = () => {
  const [count, setCount] = useAtom(scopedCounterAtom);

  return (
    <div>
      <h2>Scoped Count: {count}</h2>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
    </div>
  );
};

export default ScopedCounter;

Step 3: App with Scoped Providers

// src/App.tsx
import React from 'react';
import { Provider } from 'jotai';
import ScopedCounter from './components/ScopedCounter';

const CounterScope1 = Symbol('CounterScope1');
const CounterScope2 = Symbol('CounterScope2');

const App: React.FC = () => {
  return (
    <div>
      <h1>Scoped Providers with Jotai</h1>
      <Provider scope={CounterScope1}>
        <ScopedCounter />
      </Provider>

      <Provider scope={CounterScope2}>
        <ScopedCounter />
      </Provider>
    </div>
  );
};

export default App;

Accessing Jotai Atoms Outside Components (Final Section)

Sometimes, you might need to interact with atoms outside of React components—perhaps in utility functions or side effects. Using the getDefaultStore method, you can directly get or set values in Jotai atoms.

Example: Using get and set Outside a Component

  1. Set up a Jotai store:

    // src/utils/jotaiStore.ts
    import { getDefaultStore } from 'jotai';
    
    const jotaiStore = getDefaultStore();
    export default jotaiStore;
    
  2. Create a utility function that uses the store:

    // src/utils/incrementCounter.ts
    import jotaiStore from './jotaiStore';
    import { counterAtom } from '../atoms/counterAtom';
    
    export function incrementCounter() {
      const currentCount = jotaiStore.get(counterAtom);
      jotaiStore.set(counterAtom, currentCount + 1);
    }
    
  3. Using the function:

You can now call incrementCounter() from anywhere in your app to increment counterAtom without directly involving React components.

Conclusion

With Jotai and TypeScript, you can build a finely-tuned state management layer that’s both minimal and powerful. This guide has covered the essentials, from basic and dependent atoms to asynchronous handling with loadable and using atoms outside components. Now you’re equipped to harness Jotai’s flexibility for creating stateful, reactive apps in a way that’s both scalable and efficient.


This content originally appeared on DEV Community and was authored by pgangwani


Print Share Comment Cite Upload Translate Updates
APA

pgangwani | Sciencx (2024-10-25T21:30:48+00:00) Advanced State Management in React with Jotai (TypeScript). Retrieved from https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/

MLA
" » Advanced State Management in React with Jotai (TypeScript)." pgangwani | Sciencx - Friday October 25, 2024, https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/
HARVARD
pgangwani | Sciencx Friday October 25, 2024 » Advanced State Management in React with Jotai (TypeScript)., viewed ,<https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/>
VANCOUVER
pgangwani | Sciencx - » Advanced State Management in React with Jotai (TypeScript). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/
CHICAGO
" » Advanced State Management in React with Jotai (TypeScript)." pgangwani | Sciencx - Accessed . https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/
IEEE
" » Advanced State Management in React with Jotai (TypeScript)." pgangwani | Sciencx [Online]. Available: https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/. [Accessed: ]
rf:citation
» Advanced State Management in React with Jotai (TypeScript) | pgangwani | Sciencx | https://www.scien.cx/2024/10/25/advanced-state-management-in-react-with-jotai-typescript/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.