JotaiJotai

状態
Primitive and flexible state management for React

Debugging

In basic apps, console.log can be our best friend for debugging atoms, but when applications get bigger and we have more atoms to use, logging would not be a good way of debugging atoms. Jotai provides two ways of debugging atoms, React Dev Tools and Redux Dev tools. For reading values and simple debugging, React Dev Tools might suit you, but for more complicated tasks like Time-travelling and setting values, Redux Dev Tools would be a better option.

Debug labels

It is worth mentioning that we have a concept called Debug labels in Jotai which may help us with debugging. By default each Jotai state has the label like 1:<no debugLabel> with number being internal key assigned to each atom automatically. But you can add labels to atoms to help you distinguish them more easily with debugLabel.

const countAtom = atom(0)
// countAtom's debugLabel by default is 'atom1'
if (process.env.NODE_ENV !== 'production') {
countAtom.debugLabel = 'count'
// debugLabel is 'count' now
}

Jotai provides both a Babel and a SWC plugin, that adds a debugLabel automatically to every atom, which makes things easier for us. For more info, check out jotai/babel and @swc-jotai/debug-label

Using React Dev Tools

You can use React Dev Tools to inspect Jotai state. To achieve that useDebugValue is used inside custom hooks. Keep in mind that it only works in dev mode (such as NODE_ENV === 'development').

useAtom

useAtom calls useDebugValue for atom values, so if you select the component that consumes Jotai atoms in React Dev Tools, you would see "Atom" hooks for each atom that is used in the component along with the value it has right now.

useAtomsDebugValue

useAtomsDebugValue catches all atoms in a component tree under Provider (or an entire tree for Provider-less mode), and useDebugValue for all atoms values. If you navigate to the component that has useAtomsDebugValue in the React Dev Tools, we can see a custom hook "AtomsDebugValue" which allows you to see all atom values and their dependents.

One use case is to put the hook just under the Provider component:

const DebugAtoms = () => {
useAtomsDebugValue()
return null
}
const Root = () => (
<Provider>
<DebugAtoms />
<App />
</Provider>
)

Using Redux DevTools

You can also use Redux DevTools to inspect atoms, with many features like Time-travelling and value dispatching.

useAtomDevtools

useAtomDevtools is a React hook that manages ReduxDevTools extension for a particular atom.

If you have a specific atom in mind that you may want to debug, useAtomDevtools can be a good option.

const countAtom = atom(0)
// setting countAtom.debugLabel is recommended if we have more atoms
function Counter() {
const [count, setCount] = useAtom(countAtom)
useAtomDevtools(countAtom)
}

Now if we try setCount, we can see that the Redux Dev Tools logs those changes immediately.

Time travel

Sometimes we need to switch to a specific value of our atoms' state, with Time travelling this is possible. You can hover on each action you see in the devtools and see the Jump option there, with clicking it you'd be able to switch to that specific value.

Pause

If we don't record changes on atoms, we can stop watching those using the Pausing feature.

Dispatch

It's possible to set values on atoms with the Dispatch feature. You can do that by clicking on the Show Dispatcher button. This would set the countAtoms's value to 5.

We should note that the value will be parsed by JSON.parse, so pass supported values.

useAtomsDevtools

useAtomsDevtools is a catch-all version of useAtomDevtools where it shows all atoms in the store instead of showing a specific one.

We'd recommend this hook if you want to keep track of all of your atoms in one place. It means every action on every atom that is placed in the bottom of this hook (in the React tree) will be catched by the Redux Dev Tools.

Every feature of useAtomDevtools is supported in this hook, but there's an extra feature, which includes giving more information about atoms dependents like:

{
"values": {
"atom1:count": 0,
"atom2:doubleCount": 0,
"atom3:half": 0,
"atom4:sum": 0
},
"dependents": {
"atom1:count": ["atom1:count", "atom2:doubleCount", "atom4:sum"],
"atom2:doubleCount": ["atom3:half", "atom4:sum"],
"atom3:half": ["atom4:sum"],
"atom4:sum": []
}
}

Frozen Atoms

To find bugs where you accidentally tried to mutate objects stored in atoms you could use freezeAtom or freezeAtomCreatorfrom jotai/utils bundle. Which returns atoms value that is deeply freezed with Object.freeze.

freezeAtom

freezeAtom(anAtom): AtomType

freezeAtom takes an existing atom and make it "frozen". It returns the same atom. The atom value will be deeply frozen by Object.freeze. It is useful to find bugs where you unintentionally tried to change objects (states) which can lead to unexpected behavior. You may use freezeAtom with all atoms to prevent this situation.

Parameters

anAtom (required): An atom you wish to freeze.

Examples

import { atom } from 'jotai'
import { freezeAtom } from 'jotai/utils'
const objAtom = freezeAtom(atom({ count: 0 }))

freezeAtomCreator

If you need, you can define a factory for freezeAtom.

import { freezeAtom } from 'jotai/utils'
export function freezeAtomCreator<
CreateAtom extends (...args: unknown[]) => Atom<unknown>,
>(createAtom: CreateAtom): CreateAtom {
return ((...args: unknown[]) => freezeAtom(createAtom(...args))) as never
}