diff --git a/doc/reference/store.md b/doc/reference/store.md index b35d6af1..aef98896 100644 --- a/doc/reference/store.md +++ b/doc/reference/store.md @@ -274,6 +274,9 @@ Note that if the selector function returns a primitive type, the result of is important to define the `onUpdate` option to properly update the value manually when it changes. +Also, the return value from `useStore` is not supposed to be modified. The store +state should only be updated with actions. + ### `useDispatch` The `useDispatch` hook is useful when a component needs to be able to dispatch diff --git a/src/store.ts b/src/store.ts index 3a07c77d..5a469b03 100644 --- a/src/store.ts +++ b/src/store.ts @@ -134,8 +134,7 @@ export function useStore(selector, options: SelectorOptions = {}): any { return result[k]; }, set(target, k, v) { - result[k] = v; - return true; + throw new Error("Store state should only be modified through actions"); } }); } diff --git a/tests/store_hooks.test.ts b/tests/store_hooks.test.ts index d181d2f0..73360be6 100644 --- a/tests/store_hooks.test.ts +++ b/tests/store_hooks.test.ts @@ -100,6 +100,24 @@ describe("connecting a component to store", () => { expect(error.message).toBe("No store found when connecting 'App'"); }); + test("cannot modify state returned by usestore", async () => { + const state = { a: { b: 1 } }; + const actions = {}; + const store = new Store({ state, actions }); + + class App extends Component { + static template = xml`
`; + storeState = useStore(state => state.a); + } + + (env).store = store; + const app = new App(); + expect(app.storeState.b).toBe(1); + expect(() => (app.storeState.b = 2)).toThrow( + "Store state should only be modified through actions" + ); + }); + test("can use useStore twice in a component", async () => { const state = { a: 1, b: 2 }; const actions = {