Tuesday, June 21, 2022

Global useState hooks with Jotai

I’ve been stressing out several React global state libraries in the past months. Last week it was Jotai’s time.

I liked the concept of “atoms” and how they feel like autonomous useState parts, and how they can be share state among components. I remember trying to write something like this in the past. Jotai seems to be what I tried to do back then:

import {atom} from 'jotai';

export const nameAtom = atom('hello');

Pretty much a global useState here, which is great:

import {useAtom} from 'jotai';
import {nameAtom} from './state';
	
function App() {
	const [name, setName] = useAtom(nameAtom);

	//...
}

In large, real-world applications you’d like to write mutation methods to implement specific logic, rather than having them scattered over the components. Thus we should not make setName public; instead we should provide more specific methods.

In Jotai, while reading the state is trivial, I found writing mutations to be rather cumbersome. The syntax of “writing atoms” is, to my tired eyes, very convoluted:

export const setSurnameAtom = atom(null, (get, set, surname) => {
	set(nameAtom, get(nameAtom) + ' ' + surname);
});

//...

function App() {
	const [, setSurname] = useAtom(setSurnameAtom);
}

After giving it some thought, it occurred me that since useAtom is a hook, I can compose a custom hook over it. And then it all clicked:

export function useName() {
	const [name, setName] = useAtom(nameAtom);
	return useMemo(() => ({
		value: name,
		setSurname(surname: string) {
			setName(name + ' ' + surname);
		},
	}), [name, setName]);
}

Usage of this custom hook is straightforward, crystal clear:

import {useName} from './state';
	
function App() {
	const name = useName();

	return <>
		<h1>{name.value}</h1>
		<button onClick={() => name.setSurname('foo')}>
			Set surname
		</button>
	</>;
}

This is a truly global custom useState hook. This works amazingly well with VSCode autocomplete. This is easy to read. This is beautiful.

In the custom hook above, note the use of useMemo. It cuts down a lot of the processing in inside the custom hook, and it was an insight I had after briefly talking to Daishi Kato himself, the author of Jotai, about this custom hook idea. The future useEvent hook will optimize the button call a little further.

Jotai, unfortunately, has a huge drawback of not allowing accessing atoms outside a React component.

No comments: