diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad12ec..e6af504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +## Added + +- feat: bind state methods #3 + ## [0.1.0] - 2024-04-25 ### Changed diff --git a/README.md b/README.md index a0b90f7..f1acd7a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ and open in your web browser. You can also try them in codesandbox.io: [01](https://codesandbox.io/s/github/zustandjs/zustand-valtio/tree/main/examples/01_counter) +[02](https://codesandbox.io/s/github/zustandjs/zustand-valtio/tree/main/examples/02_methods) ## Tweets diff --git a/examples/01_counter/src/app.tsx b/examples/01_counter/src/app.tsx index e9d85be..addd056 100644 --- a/examples/01_counter/src/app.tsx +++ b/examples/01_counter/src/app.tsx @@ -1,10 +1,10 @@ import { createWithProxy } from 'zustand-valtio'; -const [useCounterState, counterState] = createWithProxy({ count: 0 }); +const useCounterState = createWithProxy({ count: 0 }); const Counter = () => { const count = useCounterState((state) => state.count); - const inc = () => ++counterState.count; + const inc = () => ++useCounterState.proxyState.count; return ( <>
Count: {count}
diff --git a/examples/02_methods/package.json b/examples/02_methods/package.json new file mode 100644 index 0000000..33801d9 --- /dev/null +++ b/examples/02_methods/package.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "version": "0.0.0", + "private": true, + "type": "commonjs", + "dependencies": { + "@types/react": "latest", + "@types/react-dom": "latest", + "react": "latest", + "react-dom": "latest", + "react-scripts": "latest", + "typescript": "latest", + "valtio": "latest", + "zustand": "latest", + "zustand-valtio": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + } +} diff --git a/examples/02_methods/public/index.html b/examples/02_methods/public/index.html new file mode 100644 index 0000000..ad4c782 --- /dev/null +++ b/examples/02_methods/public/index.html @@ -0,0 +1,8 @@ + + + example + + +
+ + diff --git a/examples/02_methods/src/app.tsx b/examples/02_methods/src/app.tsx new file mode 100644 index 0000000..841f638 --- /dev/null +++ b/examples/02_methods/src/app.tsx @@ -0,0 +1,29 @@ +import { createWithProxy } from 'zustand-valtio'; + +const useCounterState = createWithProxy({ + count: 0, + inc() { + this.count++; + }, +}); + +const Counter = () => { + const count = useCounterState((state) => state.count); + const inc = useCounterState((state) => state.inc); + return ( + <> +
Count: {count}
+ + + ); +}; + +const App = () => ( +
+ +
+); + +export default App; diff --git a/examples/02_methods/src/index.tsx b/examples/02_methods/src/index.tsx new file mode 100644 index 0000000..10774d1 --- /dev/null +++ b/examples/02_methods/src/index.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + +import App from './app'; + +const ele = document.getElementById('app'); +if (ele) { + createRoot(ele).render( + + + , + ); +} diff --git a/package.json b/package.json index 36b49f1..852dad2 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "test:types": "tsc -p . --noEmit", "test:types:examples": "tsc -p examples --noEmit", "test:spec": "vitest run", - "examples:01_counter": "DIR=01_counter vite" + "examples:01_counter": "DIR=01_counter vite", + "examples:02_methods": "DIR=02_methods vite" }, "keywords": [ "react", diff --git a/src/index.ts b/src/index.ts index 78dd3a4..a025b7f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,13 +2,23 @@ import { createStore, useStore } from 'zustand'; import { proxy, snapshot, subscribe } from 'valtio/vanilla'; import type { INTERNAL_Snapshot as Snapshot } from 'valtio/vanilla'; -export function createWithProxy(initialObject: T) { +export function createWithProxy( + initialObject: T, +): { + (selector: (state: Snapshot) => Slice): Slice; + proxyState: T; +} { const proxyState = proxy(initialObject); + Object.keys(proxyState as object).forEach((key) => { + const fn = (proxyState as Record)[key]; + if (typeof fn === 'function') { + (proxyState as Record)[key] = fn.bind(proxyState); + } + }); const store = createStore(() => snapshot(proxyState)); - const unsubscribe = subscribe(proxyState, () => - store.setState(snapshot(proxyState), true), - ); + subscribe(proxyState, () => store.setState(snapshot(proxyState), true)); const useProxyState = (selector: (state: Snapshot) => Slice) => useStore(store, selector); - return [useProxyState, proxyState, unsubscribe] as const; + useProxyState.proxyState = proxyState; + return useProxyState; }