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
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-inuseState
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);
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>;
}
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);
}
}
);
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>
);
}
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)
I have used Jotai on a project at work, and it was delightful.