import { atom, useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
// ============================================
// Atoms
// ============================================
// Primitive Atoms
export const countAtom = atom(0);
export const textAtom = atom("hello");
// Derived Atoms (Computed)
export const doubleCountAtom = atom((get) => get(countAtom) * 2);
export const uppercaseTextAtom = atom(
(get) => get(textAtom).toUpperCase(),
(get, set, newText: string) => set(textAtom, newText.toLowerCase()), // Writeable derived atom
);
// Persisted Atom (localStorage)
export const themeAtom = atomWithStorage<"light" | "dark">("theme", "light");
// Async Atom
export const usersAtom = atom(async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
return await res.json();
});
// Action Atoms (Write-only)
export const incrementCountAtom = atom(
null, // getter (null = write-only)
(get, set) => set(countAtom, get(countAtom) + 1),
);
// ============================================
// Components
// ============================================
export const Counter = () => {
const [count, setCount] = useAtom(countAtom);
const [doubleCount] = useAtom(doubleCountAtom);
return (
<div>
<h1>Count: {count} </h1>
<h2> Double: {doubleCount} </h2>
<button onClick={() => setCount((c) => c + 1)}> Increment </button>
</div>
);
};
export const ThemeToggle = () => {
const [theme, setTheme] = useAtom(themeAtom);
return (
<button
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
style={{
background: theme === "dark" ? "#333" : "#fff",
color: theme === "dark" ? "#fff" : "#000",
}}
>
Current Theme: {theme}
</button>
);
};
export const UserList = () => {
// Jotai Suspense support
const [users] = useAtom(usersAtom);
return (
<ul>
{users.map((user: any) => (
<li key={user.id}> {user.name} </li>
))}
</ul>
);
};