derive-zustand
Version:
A function to create a derived Zustand store from stores
76 lines (75 loc) • 2.33 kB
JavaScript
export function derive(deriveFn) {
const listeners = new Set();
const subscriptions = new Map();
let state;
let dependencies;
let invalidated = true;
const invalidate = () => {
if (invalidated) {
return;
}
invalidated = true;
listeners.forEach((listener) => listener(state, state));
};
const getState = () => {
if (!invalidated) {
return state;
}
if (!dependencies ||
Array.from(dependencies).some(([store, value]) => !Object.is(store.getState(), value))) {
const newDependencies = new Map();
const get = (store) => {
if (!store) {
return state;
}
const s = store.getState();
newDependencies.set(store, s);
return s;
};
state = deriveFn(get);
dependencies = newDependencies;
}
if (listeners.size) {
const deps = new Set(dependencies.keys());
subscriptions.forEach((unsubscribe, store) => {
if (deps.has(store)) {
deps.delete(store);
}
else {
unsubscribe();
subscriptions.delete(store);
}
});
deps.forEach((store) => {
subscriptions.set(store, store.subscribe(invalidate));
});
invalidated = false;
}
return state;
};
const subscribe = (listener) => {
listeners.add(listener);
return () => {
listeners.delete(listener);
if (!listeners.size) {
subscriptions.forEach((unsubscribe) => unsubscribe());
subscriptions.clear();
invalidated = true;
}
};
};
const store = {
getState,
subscribe,
getInitialState: () => {
throw new Error('getInitialState is not available in derived store');
},
setState: () => {
throw new Error('setState is not available in derived store');
},
destroy: () => {
throw new Error('destory is not available in derived store');
},
};
return store;
}