diff --git a/commitlint.config.mjs b/commitlint.config.mjs index fa584fb..fba73fb 100644 --- a/commitlint.config.mjs +++ b/commitlint.config.mjs @@ -1 +1 @@ -export default { extends: ["@commitlint/config-conventional"] }; +export default { extends: ["@commitlint/config-conventional"] } diff --git a/package.json b/package.json index b2dc9a3..e3d7563 100644 --- a/package.json +++ b/package.json @@ -31,20 +31,8 @@ } }, "description": "A Zustand middleware to create computed states.", - "files": [ - "/dist" - ], - "keywords": [ - "zustand", - "computed", - "calculated", - "state", - "react", - "plugin", - "middleware", - "npm", - "typescript" - ], + "files": ["/dist"], + "keywords": ["zustand", "computed", "calculated", "state", "react", "plugin", "middleware", "npm", "typescript"], "license": "MIT", "scripts": { "check": "biome check --write", diff --git a/src/computed.test.ts b/src/computed.test.ts index cf274fe..5980562 100644 --- a/src/computed.test.ts +++ b/src/computed.test.ts @@ -1,35 +1,35 @@ -import { describe, expect, test, beforeEach, mock } from "bun:test"; -import { type StateCreator, create } from "zustand"; -import { type ComputedStateOpts, computed } from "./computed"; +import { describe, expect, test, beforeEach, mock } from "bun:test" +import { type StateCreator, create } from "zustand" +import { type ComputedStateOpts, computed } from "./computed" type Store = { - count: number; - x: number; - y: number; - inc: () => void; - dec: () => void; -}; + count: number + x: number + y: number + inc: () => void + dec: () => void +} type ComputedStore = { - countSq: number; + countSq: number nestedResult: { - stringified: string; - }; -}; + stringified: string + } +} function computeState(state: Store): ComputedStore { const nestedResult = { stringified: JSON.stringify(state.count), - }; + } return { countSq: state.count ** 2, nestedResult, - }; + } } describe("default config", () => { - const computeStateMock = mock(computeState); + const computeStateMock = mock(computeState) const makeStore = () => create( computed( @@ -42,52 +42,52 @@ describe("default config", () => { }), computeStateMock, ), - ); + ) - let useStore: ReturnType; + let useStore: ReturnType beforeEach(() => { - computeStateMock.mockClear(); - useStore = makeStore(); - }); + computeStateMock.mockClear() + useStore = makeStore() + }) test("computed works on simple counter example", () => { // note: this function should have been called once on store creation - expect(computeStateMock).toHaveBeenCalledTimes(1); - expect(useStore.getState().count).toEqual(1); - expect(useStore.getState().countSq).toEqual(1); - useStore.getState().inc(); - expect(useStore.getState().count).toEqual(2); - expect(useStore.getState().countSq).toEqual(4); - useStore.getState().dec(); - expect(useStore.getState().count).toEqual(1); - expect(useStore.getState().countSq).toEqual(1); - useStore.setState({ count: 4 }); - expect(useStore.getState().countSq).toEqual(16); - expect(computeStateMock).toHaveBeenCalledTimes(4); - }); + expect(computeStateMock).toHaveBeenCalledTimes(1) + expect(useStore.getState().count).toEqual(1) + expect(useStore.getState().countSq).toEqual(1) + useStore.getState().inc() + expect(useStore.getState().count).toEqual(2) + expect(useStore.getState().countSq).toEqual(4) + useStore.getState().dec() + expect(useStore.getState().count).toEqual(1) + expect(useStore.getState().countSq).toEqual(1) + useStore.setState({ count: 4 }) + expect(useStore.getState().countSq).toEqual(16) + expect(computeStateMock).toHaveBeenCalledTimes(4) + }) test("computed does not modify object ref even after change", () => { - useStore.setState({ count: 4 }); - expect(useStore.getState().count).toEqual(4); - const obj = useStore.getState().nestedResult; - useStore.setState({ count: 4 }); - const toCompare = useStore.getState().nestedResult; - expect(obj).toEqual(toCompare); - }); + useStore.setState({ count: 4 }) + expect(useStore.getState().count).toEqual(4) + const obj = useStore.getState().nestedResult + useStore.setState({ count: 4 }) + const toCompare = useStore.getState().nestedResult + expect(obj).toEqual(toCompare) + }) test("modifying variables x and y do not trigger compute function more than once, as they are not used in compute function", () => { - expect(computeStateMock).toHaveBeenCalledTimes(1); - useStore.setState({ x: 2 }); - expect(computeStateMock).toHaveBeenCalledTimes(2); - useStore.setState({ x: 3 }); - expect(computeStateMock).toHaveBeenCalledTimes(2); - useStore.setState({ y: 2 }); - expect(computeStateMock).toHaveBeenCalledTimes(2); - }); -}); + expect(computeStateMock).toHaveBeenCalledTimes(1) + useStore.setState({ x: 2 }) + expect(computeStateMock).toHaveBeenCalledTimes(2) + useStore.setState({ x: 3 }) + expect(computeStateMock).toHaveBeenCalledTimes(2) + useStore.setState({ y: 2 }) + expect(computeStateMock).toHaveBeenCalledTimes(2) + }) +}) describe("custom config", () => { - const computeStateMock = mock(computeState); + const computeStateMock = mock(computeState) const makeStore = (opts?: ComputedStateOpts) => create( computed( @@ -101,54 +101,54 @@ describe("custom config", () => { computeStateMock, opts, ), - ); + ) beforeEach(() => { - computeStateMock.mockClear(); - }); + computeStateMock.mockClear() + }) test("computed does not update when a custom key selector is given", () => { - const useStore = makeStore({ keys: ["x", "y"] }); + const useStore = makeStore({ keys: ["x", "y"] }) // because we only care about x and y, the compute function should not be called when count changes - expect(computeStateMock).toHaveBeenCalledTimes(1); - expect(useStore.getState().count).toEqual(1); - expect(useStore.getState().countSq).toEqual(1); - useStore.getState().inc(); - expect(useStore.getState().count).toEqual(2); - expect(useStore.getState().countSq).toEqual(1); - useStore.getState().dec(); - expect(useStore.getState().count).toEqual(1); - expect(useStore.getState().countSq).toEqual(1); - expect(computeStateMock).toHaveBeenCalledTimes(1); - }); + expect(computeStateMock).toHaveBeenCalledTimes(1) + expect(useStore.getState().count).toEqual(1) + expect(useStore.getState().countSq).toEqual(1) + useStore.getState().inc() + expect(useStore.getState().count).toEqual(2) + expect(useStore.getState().countSq).toEqual(1) + useStore.getState().dec() + expect(useStore.getState().count).toEqual(1) + expect(useStore.getState().countSq).toEqual(1) + expect(computeStateMock).toHaveBeenCalledTimes(1) + }) test("disabling proxy causes compute to run every time", () => { - const useStore = makeStore({ disableProxy: true }); - expect(computeStateMock).toHaveBeenCalledTimes(1); - useStore.setState({ count: 4 }); - useStore.setState({ x: 2 }); - useStore.setState({ y: 3 }); - expect(useStore.getState().count).toEqual(4); - expect(useStore.getState().countSq).toEqual(16); - expect(computeStateMock).toHaveBeenCalledTimes(4); - }); -}); - -type CountSlice = Pick; -type XYSlice = Pick; + const useStore = makeStore({ disableProxy: true }) + expect(computeStateMock).toHaveBeenCalledTimes(1) + useStore.setState({ count: 4 }) + useStore.setState({ x: 2 }) + useStore.setState({ y: 3 }) + expect(useStore.getState().count).toEqual(4) + expect(useStore.getState().countSq).toEqual(16) + expect(computeStateMock).toHaveBeenCalledTimes(4) + }) +}) + +type CountSlice = Pick +type XYSlice = Pick function computeSlice(state: CountSlice): ComputedStore { const nestedResult = { stringified: JSON.stringify(state.count), - }; + } return { countSq: state.count ** 2, nestedResult, - }; + } } describe("slices pattern", () => { - const computeSliceMock = mock(computeSlice); + const computeSliceMock = mock(computeSlice) const makeStore = () => { const createCountSlice: StateCreator< Store, @@ -161,40 +161,40 @@ describe("slices pattern", () => { dec: () => set((state) => ({ count: state.count - 1 })), }), computeSliceMock, - ); + ) const createXySlice: StateCreator = (set) => ({ x: 1, y: 1, // this should not trigger compute function inc: () => set((state) => ({ count: state.count + 2 })), - }); + }) return create()((...a) => ({ ...createCountSlice(...a), ...createXySlice(...a), - })); - }; + })) + } beforeEach(() => { - computeSliceMock.mockClear(); - }); + computeSliceMock.mockClear() + }) test("computed works on slices pattern example", () => { - const useStore = makeStore(); - expect(computeSliceMock).toHaveBeenCalledTimes(1); - expect(useStore.getState().count).toEqual(1); - expect(useStore.getState().countSq).toEqual(1); - useStore.getState().inc(); - expect(useStore.getState().count).toEqual(3); - expect(useStore.getState().countSq).toEqual(1); - expect(computeSliceMock).toHaveBeenCalledTimes(1); - useStore.getState().dec(); - expect(useStore.getState().count).toEqual(2); - expect(useStore.getState().countSq).toEqual(4); - expect(computeSliceMock).toHaveBeenCalledTimes(2); - useStore.setState({ count: 4 }); - expect(useStore.getState().countSq).toEqual(16); - expect(computeSliceMock).toHaveBeenCalledTimes(3); - }); -}); + const useStore = makeStore() + expect(computeSliceMock).toHaveBeenCalledTimes(1) + expect(useStore.getState().count).toEqual(1) + expect(useStore.getState().countSq).toEqual(1) + useStore.getState().inc() + expect(useStore.getState().count).toEqual(3) + expect(useStore.getState().countSq).toEqual(1) + expect(computeSliceMock).toHaveBeenCalledTimes(1) + useStore.getState().dec() + expect(useStore.getState().count).toEqual(2) + expect(useStore.getState().countSq).toEqual(4) + expect(computeSliceMock).toHaveBeenCalledTimes(2) + useStore.setState({ count: 4 }) + expect(useStore.getState().countSq).toEqual(16) + expect(computeSliceMock).toHaveBeenCalledTimes(3) + }) +})