DEV Community

Cover image for A Quick Dive into Jotai: A Minimalist's Guide to React State Management
Niraj Kumar
Niraj Kumar

Posted on

A Quick Dive into Jotai: A Minimalist's Guide to React State Management

In the ever-evolving landscape of React state management, Jotai has emerged as a powerful yet refreshingly simple solution. If you're looking for a flexible, intuitive, and boilerplate-free way to manage your application's state, this quick tutorial will get you up and running with Jotai's core concepts.

The Philosophy: Atoms as the Source of Truth

At the heart of Jotai lies the concept of atoms. Think of an atom as a small, individual piece of state. It's a "bottom-up" approach, meaning you create these granular units of state and then combine them as your application grows. This contrasts with "top-down" solutions that often involve a single, large store.

Getting Started: Installation

First things first, let's add Jotai to your React project:

npm install jotai
# or
yarn add jotai
Enter fullscreen mode Exit fullscreen mode

The Building Blocks: atom and useAtom

The two fundamental pieces you'll work with most are atom and the useAtom hook.

  • atom: This function is used to create an atom. You simply provide it with an initial value.

    import { atom } from 'jotai';
    
    // A simple counter atom with an initial value of 0
    export const countAtom = atom(0);
    
    // An atom to hold a user's name
    export const userAtom = atom({ name: 'John Doe', age: 30 });
    
  • useAtom: This hook allows your React components to interact with an atom. It works very similarly to React's built-in useState hook, returning the atom's current value and a function to update it.

    import { useAtom } from 'jotai';
    import { countAtom } from './atoms'; // Assuming your atoms are in a separate file
    
    function Counter() {
      const [count, setCount] = useAtom(countAtom);
    
      return (
        <div>
          <h1>{count}</h1>
          <button onClick={() => setCount((c) => c + 1)}>Increment</button>
        </div>
      );
    }
    

A key benefit of useAtom is that only the components that use a particular atom will re-render when its value changes, leading to optimized performance.

Derived Atoms: Computing State on the Fly

Jotai's real power shines with derived atoms. These are atoms whose values are computed based on the values of other atoms. This is incredibly useful for creating dependent state without extra boilerplate.

A derived atom is created by passing a function to the atom function. This function receives a get argument that allows it to read the value of other atoms.

import { atom } from 'jotai';
import { countAtom } from './atoms';

// A derived atom that doubles the count
export const doubleCountAtom = atom((get) => get(countAtom) * 2);
Enter fullscreen mode Exit fullscreen mode

You can then use this derived atom in your components just like any other atom, but you'll only be able to read its value.

import { useAtom } from 'jotai';
import { doubleCountAtom } from './atoms';

function DoubleCounter() {
  const [doubleCount] = useAtom(doubleCountAtom);

  return <p>Double the count is: {doubleCount}</p>;
}
Enter fullscreen mode Exit fullscreen mode

Writable Derived Atoms: Creating Actions

You can also create derived atoms that are writable. This allows you to encapsulate logic for updating other atoms, similar to how you might use action creators in other state management libraries.

A writable derived atom is created by passing a second function (the "write" function) to the atom function. This function receives get, set, and the value to be set.

import { atom } from 'jotai';
import { countAtom } from './atoms';

export const controlAtom = atom(
  (get) => get(countAtom), // The read function
  (get, set, action) => {   // The write function
    if (action === 'INCREMENT') {
      set(countAtom, get(countAtom) + 1);
    } else if (action === 'DECREMENT') {
      set(countAtom, get(countAtom) - 1);
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Your component can then use this controlAtom to perform actions:

import { useAtom } from 'jotai';
import { controlAtom } from './atoms';

function Controls() {
  const [, dispatch] = useAtom(controlAtom);

  return (
    <div>
      <button onClick={() => dispatch('INCREMENT')}>Increment</button>
      <button onClick={() => dispatch('DECREMENT')}>Decrement</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Why Choose Jotai?

  • Simplicity and Minimal API: The learning curve is gentle, and you can be productive in minutes.
  • Boilerplate-Free: No need for actions, reducers, or dispatchers for simple state management.
  • Performance: Automatic re-render optimization out of the box.
  • Flexibility: Scales from simple useState replacement to complex application state with derived atoms.
  • TypeScript Support: Excellent first-class TypeScript support.

Top comments (1)

Collapse
 
andyrosenberg profile image
AndyRosenberg

I have used Jotai on a project at work, and it was delightful.