DEV Community

Alex Spinov
Alex Spinov

Posted on

Jotai Has a Free API That Brings Atomic State Management to React

Jotai takes an atomic approach to React state — bottom-up, composable, and incredibly efficient. No stores, no reducers, no selectors.

Atoms: The Building Blocks

import { atom, useAtom } from "jotai";

// Primitive atom
const countAtom = atom(0);
const nameAtom = atom("World");

// Derived atom (computed)
const doubledAtom = atom((get) => get(countAtom) * 2);
const greetingAtom = atom((get) => `Hello, ${get(nameAtom)}!`);

// Read-write derived atom
const celsiusAtom = atom(25);
const fahrenheitAtom = atom(
  (get) => get(celsiusAtom) * 9/5 + 32,
  (get, set, newF: number) => set(celsiusAtom, (newF - 32) * 5/9)
);

function Temperature() {
  const [fahrenheit, setFahrenheit] = useAtom(fahrenheitAtom);
  return <input value={fahrenheit} onChange={e => setFahrenheit(+e.target.value)} />;
}
Enter fullscreen mode Exit fullscreen mode

Async Atoms

const urlAtom = atom("https://api.example.com/data");

const dataAtom = atom(async (get) => {
  const url = get(urlAtom);
  const res = await fetch(url);
  return res.json();
});

function DataView() {
  const [data] = useAtom(dataAtom); // Suspense-compatible!
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Enter fullscreen mode Exit fullscreen mode

atomWithStorage: Persistent State

import { atomWithStorage } from "jotai/utils";

const themeAtom = atomWithStorage("theme", "light");
const favoritesAtom = atomWithStorage<string[]>("favorites", []);

// Automatically syncs with localStorage
// Also syncs across browser tabs!
Enter fullscreen mode Exit fullscreen mode

atomFamily: Dynamic Atom Collections

import { atomFamily } from "jotai/utils";

const productAtomFamily = atomFamily((id: string) =>
  atom(async () => {
    const res = await fetch(`/api/products/${id}`);
    return res.json();
  })
);

function Product({ id }: { id: string }) {
  const [product] = useAtom(productAtomFamily(id));
  return <div>{product.title}: ${product.price}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Integration: Jotai + TanStack Query

import { atomWithQuery } from "jotai-tanstack-query";

const productsAtom = atomWithQuery(() => ({
  queryKey: ["products"],
  queryFn: async () => {
    const res = await fetch("/api/products");
    return res.json();
  },
  staleTime: 5 * 60 * 1000,
}));

function Products() {
  const [{ data, isLoading }] = useAtom(productsAtom);
  if (isLoading) return <Spinner />;
  return data.map(p => <ProductCard key={p.id} product={p} />);
}
Enter fullscreen mode Exit fullscreen mode

splitAtom: Array Management

import { splitAtom } from "jotai/utils";

const itemsAtom = atom([{ id: 1, text: "Buy milk" }, { id: 2, text: "Walk dog" }]);
const itemAtomsAtom = splitAtom(itemsAtom);

function TodoList() {
  const [itemAtoms, dispatch] = useAtom(itemAtomsAtom);
  return (
    <>
      {itemAtoms.map((itemAtom) => (
        <TodoItem key={`${itemAtom}`} itemAtom={itemAtom} />
      ))}
      <button onClick={() => dispatch({ type: "insert", value: { id: 3, text: "New" } })}>
        Add
      </button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Atomic state for scraped data? My Apify tools deliver structured data for your Jotai atoms.

Custom solution? Email spinov001@gmail.com

Top comments (0)