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>
);
};