JotaiJotai

ηŠΆζ…‹
Primitive and flexible state management for React

Split

splitAtom

The splitAtom utility is designed for scenarios where you need to obtain an atom for each element in a list. It operates on read/write atoms containing a list, returning an atom that holds a list of atoms, each corresponding to an item in the original list.

Signature

A simplified type signature for splitAtom would be:

type SplitAtom = <Item, Key>(
arrayAtom: PrimitiveAtom<Array<Item>>,
keyExtractor?: (item: Item) => Key
): Atom<Array<PrimitiveAtom<Item>>>

Key Features

  1. The returned atom contains a dispatch function in the write direction (since v1.6.4), providing a simple way to modify the original atom with actions like remove, insert, and move.

  2. An optional keyExtractor function can be provided as a second argument to enhance stability and performance.

Key Extractor

The splitAtom utility supports a second argument which is a key extractor function:

export function splitAtom<Item, Key>(
arrAtom: WritableAtom<Item[], [Item[]], void> | Atom<Item[]>,
keyExtractor?: (item: Item) => Key,
)

Important considerations for the keyExtractor:

  • If splitAtom is used within a React render loop, the keyExtractor must be a stable function that maintains object equality (shallow equality). This requirement doesn't apply outside of render loops.
  • Providing a keyExtractor enhances the stability of the atom output (which makes memoization hit cache more often). It prevents unnecessary subatom recreation due to index shifts in the source array.
  • keyExtractor is an optional optimization that should only be used if the extracted key is guaranteed unique for each item in the array.

Example Usage

Here's an example demonstrating the use of splitAtom:

import { Provider, atom, useAtom, PrimitiveAtom } from 'jotai'
import { splitAtom } from 'jotai/utils'
import './styles.css'
const initialState = [
{
task: 'help the town',
done: false,
},
{
task: 'feed the dragon',
done: false,
},
]
const todosAtom = atom(initialState)
const todoAtomsAtom = splitAtom(todosAtom)
type TodoType = (typeof initialState)[number]
const TodoItem = ({
todoAtom,
remove,
}: {
todoAtom: PrimitiveAtom<TodoType>
remove: () => void
}) => {
const [todo, setTodo] = useAtom(todoAtom)
return (
<div>
<input
value={todo.task}
onChange={(e) => {
setTodo((oldValue) => ({ ...oldValue, task: e.target.value }))
}}
/>
<input
type="checkbox"
checked={todo.done}
onChange={() => {
setTodo((oldValue) => ({ ...oldValue, done: !oldValue.done }))
}}
/>
<button onClick={remove}>remove</button>
</div>
)
}
const TodoList = () => {
const [todoAtoms, dispatch] = useAtom(todoAtomsAtom)
return (
<ul>
{todoAtoms.map((todoAtom) => (
<TodoItem
todoAtom={todoAtom}
remove={() => dispatch({ type: 'remove', atom: todoAtom })}
/>
))}
</ul>
)
}
const App = () => (
<Provider>
<TodoList />
</Provider>
)
export default App

This example demonstrates how to use splitAtom to manage a list of todo items, allowing individual manipulation of each item while maintaining the overall list atom.