diff --git a/app/.vitepress/theme/components/example.vue b/app/.vitepress/theme/components/example.vue index 2fccae1..7d2007e 100644 --- a/app/.vitepress/theme/components/example.vue +++ b/app/.vitepress/theme/components/example.vue @@ -25,7 +25,7 @@ const wrapperRef = ref(); const codeRef = ref(); -const useCount = createState(() => ({ count: 0 }), { withActions: (s) => ({ add: () => s.count++ }), withNamespace: 'example_count', withStableSelector: true }) +const useCount = createState(() => ({ count: 0 }), { withActions: (s) => ({ add: () => s.count++ }), withNamespace: 'example_count', withStableSelector: true }); const source = ` import { createState } from 'reactivity-store'; @@ -82,7 +82,7 @@ onBeforeUnmount(() => app.unmount()) z-index: 1; left: 50%; top: 50%; - padding: 15px 10px; + padding: 15px 16px; width: 90%; transform: translateX(-50%) translateY(-50%); border-radius: 8px; diff --git a/app/createState.md b/app/createState.md index 76c2a0c..86ec414 100644 --- a/app/createState.md +++ b/app/createState.md @@ -13,21 +13,29 @@ the state which in the `selector` function is a readonly state, so the only way to change state is in the `action` middleware function. ## v0.2.4 update + new middleware `withDeepSelector` for `createState` support config the selector behavior ## v0.2.6 update + new middleware `withNamespace` for `createState` support `reduxDevTools` +## v0.3.5 update + +middleware `withDeepSelector` change to `withSelectorOptions`, also add `stableSelector` config for this middleware + ## All build in middleware for `createState` -1. `withPersist(setup, options)` make the state auto sync to storage when some data change -2. `withActions(setup, options)` add actions for current state, then you can get the action in the `selector` function -3. `withNamespace(setup, options)` make the state and change action tracked by `reduxDevTools`, you need install `redux-devtools-extension` on your browser first -4. `withDeepSelector(setup, options)` make the selector support deep selector, when the deep state change, the selector will also be trigger, the default value for the `withDeepSelector` is `true` -5. `withComputed(setup, options)` TODO (maybe won't) + +1. `withPersist(setup, options)` make the state auto sync to storage when some data change +2. `withActions(setup, options)` add actions for current state, then you can get the action in the `selector` function +3. `withNamespace(setup, options)` make the state and change action tracked by `reduxDevTools`, you need install `redux-devtools-extension` on your browser first +4. `withDeepSelector(setup, options)` make the selector support deep selector, when the deep state change, the selector will also be trigger, the default value for the `withDeepSelector` is `true` +5. `withComputed(setup, options)` TODO (maybe won't) ## Simple Code Example -```tsx +```tsx twoslash +import * as React from "react"; import { createState } from "reactivity-store"; // a simple `createState` store, there are not any change function, so the state will never change @@ -65,10 +73,11 @@ const App = () => { ## Code Example with localStorage middleware -```tsx +```tsx twoslash +import * as React from "react"; import { createState, withPersist } from "reactivity-store"; -const useCount = createState( +const _useCount = createState( withPersist( () => { const data = { count: 0 }; @@ -82,7 +91,7 @@ const useCount = createState( * parse?: (s: string) => T; // how to parse the string state to object * merge?: (fromCreator: T, fromStorage: Partial) => T; // merge two part of state to the final state */ - { key: "count" } + { key: "count", getStorage: undefined, stringify: undefined, parse: undefined, merge: undefined, shallow: undefined } ) ); @@ -117,10 +126,11 @@ const App = () => { ## Code Example with action middleware -```tsx +```tsx twoslash +import * as React from "react"; import { createState, withActions } from "reactivity-store"; -const useCount = createState( +const _useCount = createState( withActions( () => { const data = { count: 0 }; @@ -157,28 +167,20 @@ const App = () => { ::: details Click to show zustand code with same logic -```tsx +```tsx twoslash // r-store import { createState } from "reactivity-store"; -const useCount = createState( - () => { - const data = { count: 0 }; - - return data; - }, - { withActions: (state) => ({ add: () => state.count++, del: () => state.count-- }) } -); +const useCount_1 = createState(() => ({ count: 0 }), { withActions: (state) => ({ add: () => state.count++, del: () => state.count-- }) }); // zustand import { create } from "zustand"; -const useCount = create( - (set, get) => ({ - data: { count: 0 }, - add: () => set((state) => ({ data: { count: state.data.count + 1 } })), - del: () => set((state) => ({ data: { count: state.data.count - 1 } })), - }) -) +const useCount_2 = create<{ data: { count: number } }>((set, get) => ({ + data: { count: 0 }, + add: () => set((state) => ({ data: { count: state.data.count + 1 } })), + del: () => set((state) => ({ data: { count: state.data.count - 1 } })), +})); ``` + ::: ## Online Example @@ -187,20 +189,27 @@ const useCount = create( ## Code Example with all the middleware -```tsx -import { createState, withActions, withPersist } from "reactivity-store"; - -const useCount = createState( - withActions( - withPersist( - () => { - const data = { count: 0 }; - - return data; - }, - { key: "foo" } +```tsx twoslash +import * as React from "react"; +import { createState, withActions, withPersist, withSelectorOptions, withNamespace } from "reactivity-store"; + +const _useCount = createState( + withSelectorOptions( + withNamespace( + withActions( + withPersist( + () => { + const data = { count: 0 }; + + return data; + }, + { key: "foo" } + ), + { generateActions: (state) => ({ add: () => state.count++, del: () => state.count-- }) } + ), + { namespace: "_useCount", reduxDevTool: true, shallow: true } ), - { generateActions: (state) => ({ add: () => state.count++, del: () => state.count-- }) } + { stableSelector: true, deepSelector: false } ) ); @@ -212,7 +221,13 @@ const useCount = createState( return data; }, - { withActions: (state) => ({ add: () => state.count++, del: () => state.count-- }), withPersist: "foo" } + { + withActions: (state) => ({ add: () => state.count++, del: () => state.count-- }), + withPersist: "foo", + withDeepSelector: false, + withStableSelector: true, + withNamespace: "useCount", + } ); const App = () => { @@ -234,7 +249,8 @@ const App = () => { ## Code Example with deepSelector -```tsx +```tsx twoslash +import * as React from "react"; import { createState, withActions, withPersist } from "reactivity-store"; const useCount = createState( @@ -256,11 +272,11 @@ const useCount = createState( * * the default value for the `withDeepSelector` is true */ - withDeepSelector: true; + withDeepSelector: true, } ); -const App = () => { +const Foo = () => { // the `withDeepSelector` option is true, the selector will be trigger when the `re.count` state change, so the component will update normally const { re, add } = useCount((state) => ({ re: state.re, add: state.add })); @@ -283,11 +299,11 @@ const useCount_2 = createState( { generateActions: (state) => ({ add: () => state.re.count++, del: () => state.re.count-- }) } ), { - withDeepSelector: false; + withDeepSelector: false, } ); -const App = () => { +const Bar = () => { //the `withDeepSelector` option is false, the selector will not be trigger when the `re.count` state change, so the component will not update const { re, add } = useCount_2((state) => ({ re: state.re, add: state.add })); diff --git a/app/createStore.md b/app/createStore.md index 32aec3b..9b3a2d0 100644 --- a/app/createStore.md +++ b/app/createStore.md @@ -15,7 +15,7 @@ the state which in the `selector` function is a readonly state, so the only way ## Code Example ```tsx twoslash -import * as React from 'react'; +import * as React from "react"; import { createStore, reactive, ref } from "reactivity-store"; // here we create a `count` store @@ -61,12 +61,14 @@ const App = () => { ::: details Click to show `zustand` code with same logic -```tsx +```tsx twoslash // ==== r-store ==== // 1. use createStore import { createStore, ref } from "reactivity-store"; -const useCount = createStore(() => { + +// step1 create store +const useCount_1 = createStore(() => { const reactiveCount = ref(0); const changeCount = (c: number) => { @@ -75,16 +77,27 @@ const useCount = createStore(() => { return { reactiveCount, changeCount }; }); +// step2 use store +const { reactiveCount: c1, changeCount: _c1 } = useCount_1((s) => s); + // 2. use createState import { createState } from "reactivity-store"; -const useCount = createState(() => ({ reactiveCount: 0 }), { withActions: (s) => ({ changeCount: (c: number) => (s.reactiveCount = c) }) }); + +// step1 create store +const useCount_2 = createState(() => ({ reactiveCount: 0 }), { withActions: (s) => ({ changeCount: (c: number) => (s.reactiveCount = c) }) }); +// step2 use store +const { reactiveCount: c2, changeCount: _c2 } = useCount_2((s) => s); // ==== zustand ==== import { create } from "zustand"; -const useCount = create((set, get) => ({ + +// step1 create store +const useCount_3 = create<{ reactiveCount: number; changeCount: (c: number) => void }>((set, get) => ({ reactiveCount: 0, changeCount: (c: number) => set({ reactiveCount: c }), })); +// step2 use store +const { reactiveCount: c_3, changeCount: _c3 } = useCount_3((s) => s); ``` ::: diff --git a/app/createStoreWithLifeCycle.md b/app/createStoreWithLifeCycle.md index 01853b7..4cfd1df 100644 --- a/app/createStoreWithLifeCycle.md +++ b/app/createStoreWithLifeCycle.md @@ -8,7 +8,8 @@ So `Vue` have a lifeCycle function in the `setup` step, can we use it in `React` ## Code Example -```tsx +```tsx twoslash +import * as React from "react"; import { createStoreWithComponent, onMounted, onBeforeUpdate, onBeforeUnmount, ref } from "reactivity-store"; // just like `Vue` api @@ -17,7 +18,7 @@ const Time = createStoreWithComponent({ const timeRef = ref(new Date().toString()); const updateCountRef = ref(0); - let id; + let id: NodeJS.Timeout; onMounted(() => { id = setInterval(() => (timeRef.value = new Date().toString()), 1000); diff --git a/app/reactiveHook.md b/app/reactiveHook.md index 108f1d8..e7a31f3 100644 --- a/app/reactiveHook.md +++ b/app/reactiveHook.md @@ -2,7 +2,7 @@ ## Reactive Hook example -```tsx +```tsx twoslash import { useReactiveState, useReactiveEffect } from "reactivity-store"; const usePosition = () => { diff --git a/app/todoList.md b/app/todoList.md index cde42bd..064985a 100644 --- a/app/todoList.md +++ b/app/todoList.md @@ -2,7 +2,7 @@ ## ToDoList example -```tsx +```tsx twoslash import { createStore, ref, computed } from 'reactivity-store'; const useTodo = createStore(() => { diff --git a/package.json b/package.json index 28e96b5..55f28e8 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "tslib": "^2.7.0", "typescript": "^5.3.3", "vitepress": "^1.3.4", - "vue": "^3.4.38" + "vue": "^3.4.38", + "zustand": "^5.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65ea2f3..83258d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: vue: specifier: ^3.4.38 version: 3.4.38(typescript@5.3.3) + zustand: + specifier: ^5.0.0 + version: 5.0.0(@types/react@18.2.79)(react@18.2.0) packages/r-store: dependencies: @@ -5934,6 +5937,28 @@ packages: engines: {node: '>=10'} dev: true + /zustand@5.0.0(@types/react@18.2.79)(react@18.2.0): + resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + dependencies: + '@types/react': 18.2.79 + react: 18.2.0 + dev: true + /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: true diff --git a/readme.md b/readme.md index 77b06eb..9503fce 100644 --- a/readme.md +++ b/readme.md @@ -89,7 +89,7 @@ const App = () => { withPersist; // support auto cache the state to the `Storage` withActions; // support define the action for the state withNameSpace; // support define the namespace for the state, and support reduxDevTools in develop mode -withDeepSelector; // support deep selector for the state, can be used for performance optimization +withSelectorOptions // withDeepSelector; // support deep selector / stable selector for the state, can be used for performance optimization // TODO (maybe won't) withComputed; // support define the computed value for the state @@ -288,6 +288,10 @@ const useCount = createState( ); ``` +# v0.3.5 update + +### `withDeepSelector` change to `withSelectorOptions`, also add `stableSelector` config + or ```tsx