The idea is to sync two Jotai atoms by basically making writing to synced atom to write to original atom.
- source atom - atom that is declared/used higher in React tree
- target atom - atom that should be synced with source atom meaning that writing to it will write the same value to the source atom
Backwards should work too. Writing to source atom should change value in target atom
- If the atom is not synced writing to it means writing to original atom
- If the atom is target atom then writing operation writes to associated source atom
- If the atom is source atom then writing operation writes to the atom and target atom is updated automatically because target atom is basically the reference to source atom
import { atom, useAtom } from 'jotai';
import { SyncScopeProvider } from 'jotai-sync-scope';
const sourceAtom = atom(0);
const targetAtom = atom(0);
const App = () => {
const [state] = useAtom(sourceAtom);
return (
<>
<p>Source: {state}</p>
<Sync />
</>
);
};
const Sync = (props) => {
return (
<SyncScopeProvider atoms={[[sourceAtom, targetAtom]]}>
<Component />
</SyncScopeProvider>
);
};
const Component = () => {
const [state, setState] = useAtom(targetAtom);
return (
<>
<p>Target: {state}</p>
<button onClick={() => setState((s) => s + 1)}>+1</button>
</>
);
};
One of the use cases is when you have atom with array of items and you want to provide a way to consume item within scope of some components using splitAtom
and hook to the item like it's global.
import { atom, useAtom, useAtomValue, atom } from 'jotai';
import { splitAtom } from 'jotai/utils';
import { SyncScopeProvider } from 'jotai-sync-scope';
const tabsAtom = atom([]);
const tabAtomsAtom = splitAtom(tabsAtom);
const App = () => {
const tabAtoms = useAtomValue(tabAtomsAtom);
return (
<>
{tabAtoms.map((atom) => (
<SyncScopeProvider atoms={[[atom, tabAtom]]} key={atom.key}>
<Tab />
</SyncScopeProvider>
))}
</>
);
};
const tabAtom = atom({});
const Tab = () => {
const [tab, setTab] = useAtom(tabAtom);
// You can update tabAtom value as if it's global but it's actually relates to an item from tabsAtom.
};
// You can even derive some state from tabAtom and use it to access some part of it
const tabName = atom((get) => get(tabAtom).title);