Skip to content

About

A Mutative extension for Jotai

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Repository files navigation

jotai-mutative

Node CI npm license

A Mutative extension for Jotai

With the Mutative extension, you can simplify the handling of immutable data in Jotai in a mutable way, allowing you to use immutable state more conveniently.

jotai-mutative is more than 10x faster than jotai-immer. Read more about the performance comparison in Mutative.

Installation

In order to use the Mutative extension in Jotai, you will need to install Mutative and Jotai as a direct dependency.

npm install jotai mutative jotai-mutative
# Or use any package manager of your choice.
jotai-mutative mutative jotai
2.x 2.x 2.x, 3.x
1.x 1.x 2.x

Jotai 3 needs React 18 or later, so stay on Jotai 2 with React 17. Jotai 3 has no UMD build, so the UMD build of jotai-mutative, which reads the jotaiVanilla and jotaiReact globals of the UMD builds of Jotai 2, works only with Jotai 2. See the Jotai v3 migration guide for the other changes in Jotai 3.

Upgrading to Mutative v2

jotai-mutative 2.x declares mutative ^2.0.0 as its peer dependency, so upgrade both together:

npm install jotai-mutative@2 mutative@2

The API is unchanged: the atoms and hooks take the same recipes and the same Mutative options. The changes that can affect an atom are:

  • With enableAutoFreeze: true, Map and Set instances in the atom value are frozen too, so adding a property to them or replacing one of their methods fails.
  • useMutativeAtom() and useSetMutativeAtom() without Mutative options pass the recipe to an atom from atomWithMutative() or withMutative(), so the Mutative options of the atom apply to it.
  • Mutative selects its development or production build from process.env.NODE_ENV. Define it in every bundle, including bundles for Node.js, and in production, match errors by their Minified Mutative error #<code> codes instead of their messages.
  • Mutative targets ES2018; transpile it for older engines. Without a bundler, map mutative to its production ESM build, mutative/dist/mutative.esm.production.min.mjs, as its default ESM build reads process, or load its UMD build, which defines the Mutative global that the UMD build of jotai-mutative reads.
  • The type declarations of Mutative support TypeScript 5.0 and later.

See the migration guide for every change in Mutative v2.

Usage

atomWithMutative

atomWithMutative creates a new atom similar to the regular atom with a different writeFunction. In this bundle, we don't have read-only atoms, because the point of these functions is the mutative create(mutability) function. The signature of writeFunction is (get, set, update: (draft: Draft<Value>) => void) => void.

import { useAtom } from 'jotai';
import { atomWithMutative } from 'jotai-mutative';

const countAtom = atomWithMutative({ value: 0 });

const Counter = () => {
  const [count] = useAtom(countAtom);
  return <div>count: {count.value}</div>;
};

const Controls = () => {
  const [, setCount] = useAtom(countAtom);
  // setCount === update : (draft: Draft<Value>) => void
  const inc = () =>
    setCount((draft) => {
      ++draft.value;
    });
  return <button onClick={inc}>+1</button>;
};

withMutative

withMutative takes an atom and returns a derived atom, same as atomWithMutative it has a different writeFunction.

import { useAtom, atom } from 'jotai';
import { withMutative } from 'jotai-mutative';

const primitiveAtom = atom({ value: 0 });
const countAtom = withMutative(primitiveAtom);

const Counter = () => {
  const [count] = useAtom(countAtom);
  return <div>count: {count.value}</div>;
};

const Controls = () => {
  const [, setCount] = useAtom(countAtom);
  // setCount === update : (draft: Draft<Value>) => void
  const inc = () =>
    setCount((draft) => {
      ++draft.value;
    });
  return <button onClick={inc}>+1</button>;
};

useMutativeAtom

This hook takes an atom and replaces the atom's writeFunction with the new mutative-like writeFunction like the previous helpers.

import { useAtom } from 'jotai';
import { useMutativeAtom } from 'jotai-mutative';

const primitiveAtom = atom({ value: 0 });

const Counter = () => {
  const [count] = useMutativeAtom(primitiveAtom);
  return <div>count: {count.value}</div>;
};

const Controls = () => {
  const [, setCount] = useMutativeAtom(primitiveAtom);
  // setCount === update : (draft: Draft<Value>) => void
  const inc = () =>
    setCount((draft) => {
      ++draft.value;
    });
  return <button onClick={inc}>+1</button>;
};

It would be better if you don't use withMutative and atomWithMutative with useMutativeAtom because they provide the mutative-like writeFunction and we don't need to create a new one. If you do, the hook passes the recipe to the atom's writeFunction, so the Mutative options of the atom apply. With Mutative options of its own, the hook creates the next value from the draft that the atom passes, which Mutative 2 reports once in development; set the options on the atom instead. You can use useSetMutativeAtom if you need only the setter part of useMutativeAtom.

Mutative Options

Credits

jotai-mutative is inspired by jotai-immer.

It uses the same API as jotai-immer but uses Mutative under the hood. The repository is based on the jotai-immer repository.

License

jotai-mutative is MIT licensed.

About

A Mutative extension for Jotai

Topics

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages