JotaiJotai

状態
Primitive and flexible state management for React

Performance

Jotai & React gives us quite a few tools to manage the re-renders that happen in the app lifecycle. First, please read about the difference between render & commit, because that's very important to understand before going further.

Available tools & logic

There are 2 main ideas:

First is to always make heavy computation outside of the React lifecycle (in actions for example), because React works by calling your component multiple times to check for new commits to do. Always remember to make your renders as cheap as possible.

Second is to use the Jotai & React tools to prevent (heavy) re-renders.

  • You can break down large object atoms to more primitive atoms
  • selectAtom subscribe to specific part of a large object and only re-render on value change
  • focusAtom same as selectAtom, but creating a new atom for the part, giving a setter to update that specific part easily
  • useMemo & useCallback you can always use these to limit changes to a specific array of dependencies

"Stop observing" pattern

An example of pattern that can be interesting is to use useMemo to read an atom value only once, in order to prevent further renders even if the atom changes down the line.

Let's imagine a case, you have a list of toggles. Let's view 2 approaches for it:

Standard pattern

We create our store of 3 toggles set to false

const togglesAtom = atom([false, false, false])

Then, when the user clicks one toggle, we update it

const Item = ({ index, val }) => {
const setToggles = useSetAtom(togglesAtom)
const onPress = () => {
setToggles(old => [...old, [index]: !val])
}
}
const List = () => {
const [toggles] = useAtom(togglesAtom)
return toggles.map((val, index) => <Item id={index} val={val} />)
}

With this approach, updating any toggle will cause all <Item /> to re-render.

Memoized pattern

Now let's try to memoize the value on the first render

const List = () => {
const [toggles] = useMemo(() => useAtom(togglesAtom), [])
return toggles.map((val, index) => <Item id={index} initialValue={val} />)
}

But now it means we have to handle the changes locally in each Item

const Item = ({ index, initialValue }) => {
const setToggles = useSetAtom(togglesAtom)
const [toggle, setToggle] = React.useMemo() => useAtom(atom(val)), [])
const onPress = () => {
setToggle(!toggle) // Update locally
setToggles(old => [...old, [index]: !val]) // Update the main atom
}
}

Now if you update one toggle, it will only re-render the corresponding <Item />