Jotai: Atomic State Management for React

Jotai is a state management library for React applications that takes an atomic approach to global state management. The library is inspired by Recoil and uses atoms as the smallest piece of state, representing a single value. But there are already many state management libraries, why would someone pick Jotai? Because, Jotai automatically optimises renders based on atom dependency, solving the extra re-render issue of React context and eliminating the need for memoization.

Molecules are a set of atoms that can be easily scoped globally or per context. They can be a combination of atoms, derived atoms, and other molecules. They represent a more complex piece of state that can be computed from the values of its constituent atoms. And it also allows users to lift state up or down, meaning you can use molecules and atoms both independently and together to manage state more effectively in React applications.

Atoms

Atoms in Jotai are created using the atom function, which takes an initial value as an argument and returns an atom. To read and update the value of an atom, use the useAtom hook. This hook takes an atom as an argument and returns an array containing the current value of the atom and a function to update the value of the atom.

Jotai also provides some utility hooks and functions such as useAtomValue, useSetAtom, atomWithStorage, atomWithReset and many more to help with different situations. You can learn more from their website.

Let’s create a todo board using atoms for better understanding.

To begin, we need to set up our project and install its dependencies. For this example, we will be using React with Typescript and Vite. To achieve a similar project setup, run the following command:

npm init vite@latest react-jotai --template react-ts

After finishing the initialization of the project, go to the directory and install the dependencies. Then, start the dev server.

npm i && npm run dev

It's time to install Jotai and Jotai-molecules.

npm i jotai jotai-molecules 

Now that we have everything in place, let's define the states before we start working on the components. I prefer to separate functionalities to manage the state from the components. This way, you can easily update your project's views or logic without worrying about breaking something else.

Let’s create a new directory named store in the src folder and create a new file todoBoard.ts where we’ll write all of our state logic.

import { atom, useAtomValue, useSetAtom } from 'jotai';

export interface Todo {
  id: string;
  task: string;
  completed: boolean;
}

const todosAtom = atom<Todo[]>([]);
// i follow the naming convention of the main utitlity hook, so it's easy for other devs to understand what they'll be working with
// useAtomValue only returns the value rather than the default array
export const useTodosValue = () => useAtomValue(todosAtom);

// this is a write only atom, that we can use to modify other atoms
const toggleTodoAtom = atom(null, (get, set, id: string) => {
  const existingTodos = get(todosAtom);
  const updatedTodos = existingTodos.map((todo) => {
    if (todo.id === id) return { ...todo, completed: !todo.completed };
    return todo;
  });

  set(todosAtom, updatedTodos);
});
// useSetAtom only returns the function to update the atom 
export const useSetToggleTodo = () => useSetAtom(toggleTodoAtom);

const addTodoAtom = atom(null, (get, set, newTodo: Todo) => {
  const existingTodos = get(todosAtom);
  const updatedTodos = [...existingTodos, newTodo];

  set(todosAtom, updatedTodos);
});
export const useSetAddTodo = () => useSetAtom(addTodoAtom);

const removeTodoAtom = atom(null, (get, set, id: string) => {
  const existingTodos = get(todosAtom);
  const updatedTodos = existingTodos.filter((todo) => todo.id !== id);

  set(todosAtom, updatedTodos);
});
export const useSetRemoveTodo = () => useSetAtom(removeTodoAtom);

We have our state ready, let’s work on the components. I’ve created a few components to mimic bigger use cases. I’ll also be using Tailwind css to apply some basic styling to these components. To do that, I simply added this line inside the head tag in index.html file.

<script src="<https://cdn.tailwindcss.com>"></script>

Let’s create the components directory and add these three files.