Skip to content

Instantly share code, notes, and snippets.

@arkatsy
Last active May 2, 2024 00:55
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save arkatsy/7ff5b6cd95fe94b5e480972a0d116aeb to your computer and use it in GitHub Desktop.
Save arkatsy/7ff5b6cd95fe94b5e480972a0d116aeb to your computer and use it in GitHub Desktop.
How zustand works internally
import { useSyncExternalStore } from "react";
// For more on the useSyncExternalStore hook, see https://react.dev/reference/react/useSyncExternalStore
// The code is almost identical to the source code of zustand, without types and some features stripped out.
// Check the links to see the references in the source code.
// The links are referencing the v5 of the library. If you plan on reading the source code yourself v5 is the best way to start.
// The current v4 version contains lot of deprecated code and extra stuff that makes it hard to reason about if you're new to this.
// https://github.com/pmndrs/zustand/blob/fe47d3e6c6671dbfb9856fda52cb5a3a855d97a6/src/vanilla.ts#L57-L94
function createStore(createState) {
let listeners = new Set();
let state;
let initialState;
const setState = (partial) => {
const nextState = typeof partial === "function" ? partial(state) : partial;
if (!Object.is(nextState, state)) {
const previousState = state;
state = Object.assign({}, state, nextState);
listeners.forEach((listener) => listener(state, previousState));
}
};
const getState = () => state;
const getInitialState = () => initialState;
const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
const api = { setState, getState, getInitialState, subscribe };
initialState = state = createState(setState, getState, api);
return api;
}
// https://github.com/pmndrs/zustand/blob/fe47d3e6c6671dbfb9856fda52cb5a3a855d97a6/src/react.ts#L21
const identity = (state) => state;
// https://github.com/pmndrs/zustand/blob/fe47d3e6c6671dbfb9856fda52cb5a3a855d97a6/src/react.ts#L29-L40
function useStore(api, selector = identity) {
const slice = useSyncExternalStore(
api.subscribe,
() => selector(api.getState()),
() => selector(api.getInitialState())
);
return slice;
}
// https://github.com/pmndrs/zustand/blob/fe47d3e6c6671dbfb9856fda52cb5a3a855d97a6/src/react.ts#L56-L64
function create(createState) {
const api = createStore(createState);
const useBoundStore = (selector) => useStore(api, selector);
Object.assign(useBoundStore, api);
return useBoundStore;
}
// ===================================
// Usage
const useCountStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
function App() {
return (
<div>
<Counter1 />
<Counter2 />
</div>
);
}
function Counter1() {
const { count, increment, decrement } = useCountStore();
return (
<div>
<h2>Counter1</h2>
<div>{count}</div>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
function Counter2() {
const { count, increment, decrement } = useCountStore();
return (
<div>
<h2>Counter2</h2>
<div>{count}</div>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment