Introduction
npm install jotai
or yarn add jotai
Jotai is pronounced "joe-tie" and means "state" in Japanese.
You can try live demos here: Demo 1 | Demo 2.
How does Jotai differ from Recoil?
- Minimalistic API
- No string keys
- TypeScript oriented
First create a primitive atom
An atom represents a piece of state. All you need is to specify an initial value, which can be a primitive value like a string, number, object or array. You can create as many primitive atoms as you like.
import { atom } from 'jotai'const countAtom = atom(0)const countryAtom = atom('Japan')const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])const mangaAtom = atom({ 'Dragon Ball': 1984, 'One Piece': 1997, Naruto: 1999 })
Use the atom in your components
Use an atom like you'd use React.useState
:
import { useAtom } from 'jotai'function Counter() {const [count, setCount] = useAtom(countAtom)return (<h1>{count}<button onClick={() => setCount(c => c + 1)}>one up</button>
Create derived atoms with computed values
A new read-only atom can be created from existing atoms by passing a read
function as the first argument. get
allows you to fetch the contextual value
of any atom.
const doubledCountAtom = atom((get) => get(countAtom) * 2)function DoubleCounter() {const [doubledCount] = useAtom(doubledCountAtom)return <h2>{doubledCount}</h2>
Recipes
Creating an atom from atoms
You can combine atoms to create a derived atom.
const count1 = atom(1)const count2 = atom(2)const count3 = atom(3)const sum = atom((get) => get(count1) + get(count2) + get(count3))
Or if you like fp patterns ...
const atoms = [count1, count2, count3, ...otherAtoms]const sum = atom((get) => atoms.map(get).reduce((acc, count) => acc + count))
Derived async atoms
You can make the read function an async function.
const urlAtom = atom("https://json.host.com")const fetchUrlAtom = atom(async (get) => {const response = await fetch(get(urlAtom))return await response.json()})function Status() {// Re-renders the component after urlAtom changed and the async function above concludesconst [json] = useAtom(fetchUrlAtom)
You can create a writable derived atom
Specify a write function at the second argument. get
will return the current
value of an atom. set
will update an atom's value.
const decrementCountAtom = atom((get) => get(countAtom),(get, set, _arg) => set(countAtom, get(countAtom) - 1),)function Counter() {const [count, decrement] = useAtom(decrementCountAtom)return (<h1>{count}<button onClick={decrement}>Decrease</button>
Write only atoms
To make a write-only atom, don't define a read function by passing null
as the
first argument.
const multiplyCountAtom = atom(null, (get, set, by) => set(countAtom, get(countAtom) * by))function Controls() {const [, multiply] = useAtom(multiplyCountAtom)return <button onClick={() => multiply(3)}>triple</button>
Async actions
Make the write function an async function, and call set
when you're ready.
const fetchCountAtom = atom((get) => get(countAtom),async (_get, set, url) => {const response = await fetch(url)set(countAtom, (await response.json()).count)})function Controls() {const [count, compute] = useAtom(fetchCountAtom)return <button onClick={() => compute("http://count.host.com")}>compute</button>