-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(signals): throw error in dev mode on state mutation #4526
feat(signals): throw error in dev mode on state mutation #4526
Conversation
✅ Deploy Preview for ngrx-io canceled.Built without sensitive environment variables
|
ed985d4
to
4216e54
Compare
Object.freeze
in patchState
on state in dev modeObject.freeze
in patchState
on state in dev mode
4216e54
to
59bd044
Compare
That is great! Trying to modify the state would result in a runtime error. Any reasons for not returning a Readonly type that would catch it before on compile time? |
Yes, experience. It has shown that if you start to bend the type system too much, you might end up in some unpredictable issues. For example, if people provide some type, which could be any or maybe a very complicated type that we cannot predict, then we might run into issues where suddenly it stops working. That's the reasoning behind this decision. |
IMHO, I see it as complementary, don't mean type only can cover all use cases. Freezing state during development is great. But adding additional type safety can provide immediate feedback right in the IDE when writing the code. FWIW, there are simple utils that would provide deep readonly types and would cover the majority of simple cases: microsoft/TypeScript#13923 (comment) |
Question: I understand that, since the freeze logic is applied in the patch function, it will also work for the signal store, is that correct? If that is the case, I'd also recommend freezing the output of withComputed. In user land code I've faced the issue of trying to modify the state down in the component tree, which consumes derived state, not root state. Freezing the result of any read only signals coming from the store would be great. @eneajaho thoughts on freezedComputed for ngxtension? 😉 |
@samuelfernandez I think this is an issue for Angular. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a note to the docs on this behavior
To mention new behavior, we can update this alert with an additional sentence that an error will be thrown in dev mode on state mutations: https://github.com/ngrx/platform/blob/main/projects/ngrx.io/content/guide/signals/signal-state.md?plain=1#L59 |
Since this PR introduces a breaking change, we should add this to the PR description in the following format:
Check this PR for more info: #4584 |
59bd044
to
b530562
Compare
@markostanimirovic, all your requests have been incorporated:
|
@rainerhahnekamp recheck this comment: #4526 (comment) Currently, the error will be thrown only if the state is mutated within the
|
@rainerhahnekamp It will also be useful to update documentation: #4526 (comment) |
5a854c1
to
1f203b6
Compare
BREAKING CHANGE: the state of `signalStore` and `signalState` is frozen during dev mode. Mutable changes - not just in `patchState` - will throw an error. BEFORE: ```typescript const userState = signalState(initialState); patchState(userState, (state) => { state.user.firstName = 'mutable change'; // mutable change went through return state; }); getState(userState).user.firstName = 'mutable change'; // mutable change went through ``` AFTER: ``` const userState = signalState(initialState); patchState(userState, (state) => { state.user.firstName = 'mutable change'; // throws in dev mode return state; }); getState(userState).user.firstName = 'mutable change'; // throws in dev mode ```
1f203b6
to
bff9d2c
Compare
@markostanimirovic, as requested, the state itself is now frozen, not just the changes coming from I'll update the PR about the documentation as well. Commits have been squashed into a "conventional commit"-friendly message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍 Thanks Rainer!
Object.freeze
in patchState
on state in dev mode
Alternative version to protected the state from mutable changes via
Object.freeze
.Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Mutable changes in
patchState
don't trigger any kind of warningCloses #4030
What is the new behavior?
In development mode (
ngDevMode
),Object.freeze
is applied to the state, causing a runtime error on a mutable change.Does this PR introduce a breaking change?