1 | <br>
|
2 |
|
3 | ![Jotai (light mode)](./img/jotai-header-light.png#gh-light-mode-only)
|
4 |
|
5 |
|
6 | <br>
|
7 |
|
8 | `npm i jotai` or visit [jotai.org](https://jotai.org)
|
9 |
|
10 | [![Build Status](https://img.shields.io/github/workflow/status/pmndrs/jotai/Lint?style=flat&colorA=000000&colorB=000000)](https://github.com/pmndrs/jotai/actions?query=workflow%3ALint)
|
11 | [![Build Size](https://img.shields.io/bundlephobia/minzip/jotai?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=jotai)
|
12 | [![Version](https://img.shields.io/npm/v/jotai?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/jotai)
|
13 | [![Downloads](https://img.shields.io/npm/dt/jotai.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/jotai)
|
14 | [![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/poimandres)
|
15 | [![Open Collective](https://img.shields.io/opencollective/all/jotai?style=flat&colorA=000000&colorB=000000)](https://opencollective.com/jotai)
|
16 |
|
17 | Jotai is pronounced "joe-tie" and means "state" in Japanese.
|
18 |
|
19 | You can try live demos in the following:
|
20 | [Demo 1](https://codesandbox.io/s/jotai-demo-47wvh) |
|
21 | [Demo 2](https://codesandbox.io/s/jotai-demo-forked-x2g5d).
|
22 |
|
23 | #### How does Jotai differ from Recoil?
|
24 |
|
25 | - Minimalistic API
|
26 | - No string keys
|
27 | - TypeScript oriented
|
28 |
|
29 | ### First create a primitive atom
|
30 |
|
31 | An atom represents a piece of state. All you need is to specify an initial
|
32 | value, which can be primitive values like strings and numbers, objects and
|
33 | arrays. You can create as many primitive atoms as you want.
|
34 |
|
35 | ```jsx
|
36 | import { atom } from 'jotai'
|
37 |
|
38 | const countAtom = atom(0)
|
39 | const countryAtom = atom('Japan')
|
40 | const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])
|
41 | const mangaAtom = atom({ 'Dragon Ball': 1984, 'One Piece': 1997, Naruto: 1999 })
|
42 | ```
|
43 |
|
44 | ### Use the atom in your components
|
45 |
|
46 | It can be used like `React.useState`:
|
47 |
|
48 | ```jsx
|
49 | import { useAtom } from 'jotai'
|
50 |
|
51 | function Counter() {
|
52 | const [count, setCount] = useAtom(countAtom)
|
53 | return (
|
54 | <h1>
|
55 | {count}
|
56 | <button onClick={() => setCount(c => c + 1)}>one up</button>
|
57 | ```
|
58 |
|
59 | ### Create derived atoms with computed values
|
60 |
|
61 | A new read-only atom can be created from existing atoms by passing a read
|
62 | function as the first argument. `get` allows you to fetch the contextual value
|
63 | of any atom.
|
64 |
|
65 | ```jsx
|
66 | const doubledCountAtom = atom((get) => get(countAtom) * 2)
|
67 |
|
68 | function DoubleCounter() {
|
69 | const [doubledCount] = useAtom(doubledCountAtom)
|
70 | return <h2>{doubledCount}</h2>
|
71 | ```
|
72 |
|
73 | ## Recipes
|
74 |
|
75 | ### Creating an atom from multiple atoms
|
76 |
|
77 | You can combine multiple atoms to create a derived atom.
|
78 |
|
79 | ```jsx
|
80 | const count1 = atom(1)
|
81 | const count2 = atom(2)
|
82 | const count3 = atom(3)
|
83 |
|
84 | const sum = atom((get) => get(count1) + get(count2) + get(count3))
|
85 | ```
|
86 |
|
87 | Or if you like fp patterns ...
|
88 |
|
89 | ```jsx
|
90 | const atoms = [count1, count2, count3, ...otherAtoms]
|
91 | const sum = atom((get) => atoms.map(get).reduce((acc, count) => acc + count))
|
92 | ```
|
93 |
|
94 | ### Derived async atoms <img src="https://img.shields.io/badge/-needs_suspense-black" alt="needs suspense" />
|
95 |
|
96 | You can make the read function an async function too.
|
97 |
|
98 | ```jsx
|
99 | const urlAtom = atom("https://json.host.com")
|
100 | const fetchUrlAtom = atom(
|
101 | async (get) => {
|
102 | const response = await fetch(get(urlAtom))
|
103 | return await response.json()
|
104 | }
|
105 | )
|
106 |
|
107 | function Status() {
|
108 | // Re-renders the component after urlAtom changed and the async function above concludes
|
109 | const [json] = useAtom(fetchUrlAtom)
|
110 | ```
|
111 |
|
112 | ### You can create a writable derived atom
|
113 |
|
114 | Specify a write function at the second argument. `get` will return the current
|
115 | value of an atom. `set` will update an atoms value.
|
116 |
|
117 | ```jsx
|
118 | const decrementCountAtom = atom(
|
119 | (get) => get(countAtom),
|
120 | (get, set, _arg) => set(countAtom, get(countAtom) - 1),
|
121 | )
|
122 |
|
123 | function Counter() {
|
124 | const [count, decrement] = useAtom(decrementCountAtom)
|
125 | return (
|
126 | <h1>
|
127 | {count}
|
128 | <button onClick={decrement}>Decrease</button>
|
129 | ```
|
130 |
|
131 | ### Write only atoms
|
132 |
|
133 | Just do not define a read function.
|
134 |
|
135 | ```jsx
|
136 | const multiplyCountAtom = atom(null, (get, set, by) => set(countAtom, get(countAtom) * by))
|
137 |
|
138 | function Controls() {
|
139 | const [, multiply] = useAtom(multiplyCountAtom)
|
140 | return <button onClick={() => multiply(3)}>triple</button>
|
141 | ```
|
142 |
|
143 | ### Async actions
|
144 |
|
145 | Just make the write function an async function and call `set` when you're ready.
|
146 |
|
147 | ```jsx
|
148 | const fetchCountAtom = atom(
|
149 | (get) => get(countAtom),
|
150 | async (_get, set, url) => {
|
151 | const response = await fetch(url)
|
152 | set(countAtom, (await response.json()).count)
|
153 | }
|
154 | )
|
155 |
|
156 | function Controls() {
|
157 | const [count, compute] = useAtom(fetchCountAtom)
|
158 | return <button onClick={() => compute("http://count.host.com")}>compute</button>
|
159 | ```
|
160 |
|
161 | ## Installation notes
|
162 |
|
163 | This package requires some peer dependencies, which you need to install by
|
164 | yourself.
|
165 |
|
166 | ```bash
|
167 | yarn add jotai react
|
168 | ```
|
169 |
|
170 | ## Learn Jotai
|
171 |
|
172 | [![free egghead Jotai introduction course by Daishi](./img/jotai-course-banner.jpg)](https://egghead.io/courses/manage-application-state-with-jotai-atoms-2c3a29f0?utm_source=github&utm_medium=cta&utm_term=jotai)
|
173 |
|
174 | ## More documents
|
175 |
|
176 | - Basics
|
177 | - [Concepts](./docs/basics/concepts.mdx)
|
178 | - [Primitives](./docs/basics/primitives.mdx)
|
179 | - [Comparison](./docs/basics/comparison.mdx)
|
180 | - [Showcase](./docs/basics/showcase.mdx)
|
181 | - Guides
|
182 | - [Async](./docs/guides/async.mdx)
|
183 | - [TypeScript](./docs/guides/typescript.mdx)
|
184 | - [Debugging](./docs/guides/debugging.mdx)
|
185 | - [Testing](./docs/guides/testing.mdx)
|
186 | - [Persistence](./docs/guides/persistence.mdx)
|
187 | - [Next.js](./docs/guides/nextjs.mdx)
|
188 | - [React Native](./docs/guides/react-native.mdx)
|
189 | - [Resettable](./docs/guides/resettable.mdx)
|
190 | - [Atoms in atom](./docs/guides/atoms-in-atom.mdx)
|
191 | - API
|
192 | - [Core](./docs/api/core.mdx)
|
193 | - [Utils](./docs/api/utils.mdx)
|
194 | - [Devtools](./docs/api/devtools.mdx)
|
195 | - [Babel](./docs/api/babel.mdx)
|
196 | - Integrations
|
197 | - [Immer](./docs/integrations/immer.mdx)
|
198 | - [Optics](./docs/integrations/optics.mdx)
|
199 | - [Query](./docs/integrations/query.mdx)
|
200 | - [XState](./docs/integrations/xstate.mdx)
|
201 | - [Valtio](./docs/integrations/valtio.mdx)
|
202 | - [Zustand](./docs/integrations/zustand.mdx)
|
203 | - [Redux](./docs/integrations/redux.mdx)
|
204 | - [URQL](./docs/integrations/urql.mdx)
|
205 | - Advanced Recipes
|
206 | - [Large Objects](./docs/advanced-recipes/large-objects.mdx)
|
207 | - [Atom Creators](./docs/advanced-recipes/atom-creators.mdx)
|