Jotai
Jotai的卖点
- 极简
- 灵活
- 优化多余渲染问题
- 由pmndrs的大神dai-shi开发
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
export default function JotaiDemo() {
const [count, setCount] = useAtom(countAtom);
return (
<div>
count: {count}
<button
onClick={() => {
setCount((x) => x + 1);
}}
>
+1
</button>
</div>
);
}
atom 和 useAtom
Jotai解决直接用Context多余渲染的问题
import { useState, createContext, useContext } from 'react';
const CountContext = createContext();
export default function ContextDemo() {
return (
<CountContext.Provider value={useState(0)}>
<Child />
<OtherChild />
</CountContext.Provider>
);
}
function Child() {
const [count, setCount] = useContext(CountContext);
return (
<div>
count: {count}
<button
onClick={() => {
setCount((x) => x + 1);
}}
>
+1
</button>
</div>
);
}
function OtherChild() {
return Math.random();
}
import { atom, useAtom } from 'jotai';
export default function App() {
return (
<>
<Child />
<OtherChild />
</>
);
}
const countAtom = atom(0);
function Child() {
const [count, setCount] = useAtom(countAtom);
return (
<div>
count: {count}
<button
onClick={() => {
setCount((x) => x + 1);
}}
>
+1
</button>
</div>
);
}
function OtherChild() {
return Math.random();
}
jotai demo也增加OtherChild
读写分离
import { atom, useAtomValue, useSetAtom } from 'jotai';
const countAtom = atom(0);
function Count() {
const count = useAtomValue(countAtom);
return <div>count: {count}</div>;
}
function SetCount() {
const setCount = useSetAtom(countAtom);
return (
<div>
<button onClick={() => setCount((x) => x + 1)}>+1</button>
{Math.random()}
</div>
);
}
export default function App() {
return (
<>
<Count />
<SetCount />
</>
);
}
import { useContext } from 'react';
import { memo } from 'react';
import { useState } from 'react';
import { createContext } from 'react';
const CountContext = createContext();
const CountSetContext = createContext();
const Count = memo(function Count() {
const count = useContext(CountContext);
return <div>count: {count}</div>;
});
const Button = memo(function Button() {
const setCount = useContext(CountSetContext);
return (
<div>
<button
onClick={() => {
setCount((x) => x + 1);
}}
>
+1
</button>
{Math.random()}
</div>
);
});
export default function ContextDemo() {
const [count, setCount] = useState(0);
return (
<CountSetContext.Provider value={setCount}>
<CountContext.Provider value={count}>
<Count />
<Button />
</CountContext.Provider>
</CountSetContext.Provider>
);
}
Context 读写分离
派生atom
从countAtom派生出一个jsx atom
import { atom, useAtomValue } from 'jotai';
import { useState } from 'react';
const countAtom = atom(1);
const derivedAtom = atom((get) => {
return (
<div>
{Math.random()} {get(countAtom)}
</div>
);
});
function Derived() {
return useAtomValue(derivedAtom);
}
export default function App() {
const [mount, setMount] = useState(false);
return (
<>
挂载
<input
type="checkbox"
onChange={(e) => {
setMount(e.target.checked);
}}
checked={mount}
/>
{mount ? (
<>
<Derived />
<Derived />
<Derived />
<Derived />
</>
) : null}
</>
);
}
Action atoms
import { atom, useAtomValue, useSetAtom } from "jotai";
const scoresAtom = atom({
101: 0,
102: 0,
});
const setScoreAtom = atom(null, (get, set, id, score) => {
const prevValue = get(scoresAtom);
set(scoresAtom, {
...prevValue,
[id]: typeof score === "function" ? score(prevValue[id]) : score,
});
});
const scoresAtomView = atom((get) => {
return JSON.stringify(get(scoresAtom), null, 4);
});
function ScoreView() {
return useAtomValue(scoresAtomView);
}
export default function App() {
const setScore = useSetAtom(setScoreAtom);
return (
<>
<pre>
<ScoreView />
</pre>
<div>
<button
onClick={() => {
setScore(101, (prev) => prev + 1);
}}
>
101 + 1
</button>
</div>
</>
);
}
Action Atoms:把操作单独封装
作为对比:用useStore和useCallback的实现
import { atom, useAtomValue, useStore } from 'jotai';
import { useCallback } from 'react';
const scoresAtom = atom({
101: 0,
102: 0,
});
const scoresAtomView = atom((get) => {
return JSON.stringify(get(scoresAtom), null, 4);
});
function ScoreView() {
return useAtomValue(scoresAtomView);
}
export default function App() {
const store = useStore();
const setScore = useCallback(
(id, score) => {
const { get, set } = store;
const prevValue = get(scoresAtom);
set(scoresAtom, {
...prevValue,
[id]: typeof score === 'function' ? score(prevValue[id]) : score,
});
},
[store]
);
return (
<>
<pre>
<ScoreView />
</pre>
<div>
<button
onClick={() => {
setScore(101, (prev) => prev + 1);
}}
>
101 + 1
</button>
<button
onClick={() => {
setScore(102, (prev) => prev + 1);
}}
>
102 + 1
</button>
</div>
</>
);
}
作为对比:用useAtomCallback的实现
import { atom, useAtomValue } from 'jotai';
import { useAtomCallback } from 'jotai/utils';
import { useCallback } from 'react';
const scoresAtom = atom({
101: 0,
102: 0,
});
const scoresAtomView = atom((get) => {
return JSON.stringify(get(scoresAtom), null, 4);
});
function ScoreView() {
return useAtomValue(scoresAtomView);
}
export default function App() {
const setScore = useAtomCallback(
useCallback((get, set, id, score) => {
const prevValue = get(scoresAtom);
set(scoresAtom, {
...prevValue,
[id]: typeof score === 'function' ? score(prevValue[id]) : score,
});
}, [])
);
return (
<>
<pre>
<ScoreView />
</pre>
<div>
<button
onClick={() => {
setScore(101, (prev) => prev + 1);
}}
>
101 + 1
</button>
<button
onClick={() => {
setScore(102, (prev) => prev + 1);
}}
>
102 + 1
</button>
</div>
</>
);
}
Effect
import { atomEffect } from 'jotai-effect';
import { atom, useAtom, useAtomValue } from 'jotai';
const countAtom = atom(0);
const autoCountEffect = atomEffect((get, set) => {
const timer = setInterval(() => {
set(countAtom, (prev) => prev + 1);
}, 1e3);
return () => {
clearInterval(timer);
};
});
const loggingEffect = atomEffect((get) => {
console.log(get(countAtom));
});
export default function JotaiEffect() {
useAtom(autoCountEffect);
useAtom(loggingEffect);
return useAtomValue(countAtom);
}
用jotai-effect声明effect
然而最新版jotai和jotai-effect一起用,有bug
一些实用的文档
- Store + Provider,将atom的状态存储在组件树中https://jotai.org/docs/core/store
- atom 文档:异步、onMount 属性等https://jotai.org/docs/core/atom
- 精细控制某个atom的store的作用域(
第三方库,还不稳定,jotai-scope目前有bug)https://jotai.org/docs/integrations/scope
Thank you!
jotai
By Xavier Peng
jotai
- 73