Next.js Example
Craft your next amazing library using
A tiny yet powerful store for modern react libraries.
Kosha
Extract as Object
const { p2, setP2 } = myStore(({ p2, setP2 }) => ({ p2, setP2 }))
p1: | renderCount is 1
p2:0.000000 | renderCount is 1
Extract as Array
const [p2, setP2] = myStore(({ p2, setP2 }) => [p2, setP2])
p1: | renderCount is 1
p2:0.000000 | renderCount is 1
Extract individually
const p2 = myStore(({ p2 }) => p2)
p1: | renderCount is 1
p2:0.000000 | renderCount is 1
Zustand
Extract as Object
const { p2, setP2 } = myStore(({ p2, setP2 }) => ({ p2, setP2 }))
p1: | renderCount is 1
p2:0.000000 | renderCount is 1
Extract as Array
const [p2, setP2] = myStore(({ p2, setP2 }) => [p2, setP2])
p1: | renderCount is 1
p2:0.000000 | renderCount is 1
Extract individually
const p2 = myStore(({ p2 }) => p2)
p1: | renderCount is 1
p2:0.000000 | renderCount is 1
ShowHide Code
import { useRef } from "react"; import { getStore, ICompareStore, ICompareStoreActions } from "./store"; import { ErrorBoundary } from "react-error-boundary"; import styles from "./compare.module.scss"; interface StoreTypeProps { type: "zustand" | "kosha"; } type ComponentProps = ICompareStore & ICompareStoreActions; const P1 = ({ p1, setP1 }: Pick<ComponentProps, "p1" | "setP1">) => { const renderCountRef = useRef(0); renderCountRef.current++; return ( <div> <p> p1: <button onClick={() => setP1(crypto.randomUUID())}>Update</button> {p1.slice(25)} | renderCount is {renderCountRef.current} </p> </div> ); }; const P2 = ({ p2, setP2 }: Pick<ComponentProps, "p2" | "setP2">) => { const renderCountRef = useRef(0); renderCountRef.current++; return ( <div> <p> p2: <button onClick={() => setP2(Math.random())}>Update</button> {p2.toFixed(6)} | renderCount is {renderCountRef.current} </p> </div> ); }; const P1ExtractAsObj = ({ type }: StoreTypeProps) => { const { p1, setP1 } = getStore(type)(({ p1, setP1 }) => ({ p1, setP1 })); return <P1 {...{ p1, setP1 }} />; }; const P1ExtractAsArray = ({ type }: StoreTypeProps) => { const [p1, setP1] = getStore(type)(({ p1, setP1 }) => [p1, setP1]); return <P1 {...{ p1, setP1 }} />; }; const P1ExtractIndividually = ({ type }: StoreTypeProps) => { const p1 = getStore(type)(({ p1 }) => p1); const setP1 = getStore(type)(({ setP1 }) => setP1); return <P1 {...{ p1, setP1 }} />; }; const P2ExtractAsObj = ({ type }: StoreTypeProps) => { const { p2, setP2 } = getStore(type)(({ p2, setP2 }) => ({ p2, setP2 })); return <P2 {...{ p2, setP2 }} />; }; const P2ExtractAsArray = ({ type }: StoreTypeProps) => { const [p2, setP2] = getStore(type)(({ p2, setP2 }) => [p2, setP2]); return <P2 {...{ p2, setP2 }} />; }; const P2ExtractIndividually = ({ type }: StoreTypeProps) => { const p2 = getStore(type)(({ p2 }) => p2); const setP2 = getStore(type)(({ setP2 }) => setP2); return <P2 {...{ p2, setP2 }} />; }; export const Compare = () => ( <div className={styles.compare}> <div className={styles.compare__store}> <h2>Kosha</h2> <h4>Extract as Object</h4> <code>{`const { p2, setP2 } = myStore(({ p2, setP2 }) => ({ p2, setP2 }))`}</code> <P1ExtractAsObj type="kosha" /> <P2ExtractAsObj type="kosha" /> <hr /> <h4>Extract as Array</h4> <code>{`const [p2, setP2] = myStore(({ p2, setP2 }) => [p2, setP2])`}</code> <P1ExtractAsArray type="kosha" /> <P2ExtractAsArray type="kosha" /> <hr /> <h4>Extract individually</h4> <code>{`const p2 = myStore(({ p2 }) => p2)`}</code> <P1ExtractIndividually type="kosha" /> <P2ExtractIndividually type="kosha" /> </div> <div className={styles.compare__store}> <h2>Zustand</h2> <h4>Extract as Object</h4> <code>{`const { p2, setP2 } = myStore(({ p2, setP2 }) => ({ p2, setP2 }))`}</code> <ErrorBoundary fallback={ <div> <p>⚠️Something went wrong</p> </div> }> <P1ExtractAsObj type="zustand" /> </ErrorBoundary> <ErrorBoundary fallback={ <div> <p>⚠️Something went wrong</p> </div> }> <P2ExtractAsObj type="zustand" /> </ErrorBoundary> <hr /> <h4>Extract as Array</h4> <code>{`const [p2, setP2] = myStore(({ p2, setP2 }) => [p2, setP2])`}</code> <ErrorBoundary fallback={ <div> <p>⚠️Something went wrong</p> </div> }> <P1ExtractAsArray type="zustand" /> </ErrorBoundary> <ErrorBoundary fallback={ <div> <p>⚠️Something went wrong</p> </div> }> <P2ExtractAsArray type="zustand" /> </ErrorBoundary> <hr /> <h4>Extract individually</h4> <code>{`const p2 = myStore(({ p2 }) => p2)`}</code> <P1ExtractIndividually type="zustand" /> <P2ExtractIndividually type="zustand" /> </div> </div> );
Example using persist middleware
Count: 0
Local Count: 0
ShowHide Code
import { create } from "kosha"; import { persist } from "kosha/middleware"; import styles from "../demo.module.scss"; interface CounterStore { count: number; localCount: number; setCount: (count: number) => void; setLocalCount: (count: number) => void; } const usePersistedKosha = create( persist<CounterStore>({ key: "test-kosha", partialize: state => ({ count: state.count }) })( set => ({ count: 0, localCount: 0, setCount: (count: number) => set({ count }), setLocalCount: localCount => set({ localCount }), }), ), ); export const PersistedCounter = () => { const { count, localCount, setCount, setLocalCount } = usePersistedKosha(); return ( <div className={styles.preview}> <h2>Example using persist middleware</h2> <div> Count: {count} <button onClick={() => setCount(count + 1)}>Increment</button> </div> <div> Local Count: {localCount} <button onClick={() => setLocalCount(localCount + 1)}>Increment</button> </div> </div> ); };
Example using immer middleware
Count: 0
ShowHide Code
import { create } from "kosha"; import { immer } from "kosha/middleware"; import styles from "../demo.module.scss"; interface CounterStore { count: number; setCount: (count: number) => void; } const useKoshaWithImmer = create( immer<CounterStore>(set => ({ count: 0, setCount: (count: number) => set(state => { state.count = count; }), })), ); export const CounterWithImmer = () => { const { count, setCount } = useKoshaWithImmer(); return ( <div className={styles.preview}> <h2>Example using immer middleware</h2> <div> Count: {count} <button onClick={() => setCount(count + 1)}>Increment</button> </div> </div> ); };
Example using slices to create the store.
Count: 0
Theme: light
ShowHide Code
import { create, SliceCreator } from "kosha"; import styles from "../demo.module.scss"; interface CounterSlice { count: number; setCount: (count: number) => void; } interface ThemeSlice { theme: string; setTheme: (theme: string) => void; } type StoreType = CounterSlice & ThemeSlice; const counterSlice: SliceCreator<StoreType, CounterSlice> = set => ({ count: 0, setCount: (count: number) => set({ count }), }); const createThemeSlice: SliceCreator<StoreType, ThemeSlice> = set => ({ theme: "light", setTheme: (theme: string) => set({ theme }), }); const useKosha = create<StoreType>((...a) => ({ ...createThemeSlice(...a), ...counterSlice(...a), })); export const SlicingTheStore = () => { const { count, setCount, theme, setTheme } = useKosha(); return ( <div className={styles.preview}> <h2>Example using slices to create the store.</h2> <div> Count: {count} <button onClick={() => setCount(count + 1)}>Increment</button> </div> <div> Theme: {theme} <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle Theme</button> </div> </div> ); };
Basic Example: Synced counters
All components below share the same state without any wrapper.
Counter Controller 1
Counter Display 1
0
Counter Controller 2
Counter Display 2
0
ShowHide Code
import { create } from "kosha"; interface CounterStore { count: number; setCount: (count: number) => void; } const useKosha = create<CounterStore>(set => ({ count: 0, setCount: (count: number) => set(state => ({ ...state, count })), })); /** Counter Controller */ export const CounterController = () => { const [count, setCount] = useKosha(({ count, setCount }) => [count, setCount]); return <input type="number" value={count} onChange={e => setCount(Number(e.target.value))} />; }; /** Counter Display */ export const CounterDisplay = () => { const { count } = useKosha(); return <div>{count}</div>; };
Example with Selectors
My name is John
Updates only whenname
changes. renderCount = 1
Counter With Selectors
Rerender is triggered by RGS only when count changes.
Render Count: 1
Count: 0
Counter Without Selectors
Rerender is triggered every time the state changes.
Render Count: 1
Count: 0
UserData
renderCount = 1 | Name: John | Age: 30
ShowHide Code
import { create, StateSetter } from "kosha"; interface MyKosha { count: number; name: string; user: { name: string; age: number; }; setCount: (count: number) => void; set: StateSetter<MyKosha>; } export const useMyKosha = create<MyKosha>(set => ({ count: 0, name: "John", user: { name: "John", age: 30, }, setCount: (count: number) => set(state => ({ ...state, count })), set, }));