diff --git a/projects/ngrx.io/content/guide/signals/faq.md b/projects/ngrx.io/content/guide/signals/faq.md
index f4cd6df071..05cec85d49 100644
--- a/projects/ngrx.io/content/guide/signals/faq.md
+++ b/projects/ngrx.io/content/guide/signals/faq.md
@@ -4,6 +4,7 @@
There's no official connection between `@ngrx/signals` and the Redux Devtools.
We expect the Angular Devtools will provide support for signals soon, which can be used to track the state.
However, you could create a feature for this, or you can make use of the [`withDevtools` feature](https://github.com/angular-architects/ngrx-toolkit?tab=readme-ov-file#devtools-withdevtools) from the `@angular-architects/ngrx-toolkit` package.
+
@@ -11,6 +12,7 @@
Signals are not meant to have a concept of time. Also, the effect is somewhat tied to Angular change detection, so you can't observe every action that would be dispatched over time through some sort of Signal API.
The global NgRx Store is still the best mechanism to dispatch action(s) over time and react to them across multiple features.
+
@@ -18,6 +20,7 @@
Just like `@ngrx/component-store`, there is no indirection between events and how it affects the state. To update the SignalStore's state use the `patchState` function.
However, SignalStore is extensible and you can build your own custom feature that uses the Redux pattern.
+
@@ -26,18 +29,20 @@
To create a class-based SignalStore, create a new class and extend from `signalStore`.
```ts
+
@Injectable()
export class CounterStore extends signalStore(
- { protectedState: false },
- withState({ count: 0 })
+ {protectedState: false},
+ withState({count: 0})
) {
readonly doubleCount = computed(() => this.count() * 2);
increment(): void {
- patchState(this, { count: this.count() + 1 });
+ patchState(this, {count: this.count() + 1});
}
}
```
+
@@ -46,7 +51,7 @@ export class CounterStore extends signalStore(
To get the type of a SignalStore, use the `InstanceType` utility type.
```ts
-const CounterStore = signalStore(withState({ count: 0 }));
+const CounterStore = signalStore(withState({count: 0}));
type CounterStore = InstanceType;
@@ -54,6 +59,7 @@ function logCount(store: CounterStore): void {
console.log(store.count());
}
```
+
@@ -63,16 +69,87 @@ function logCount(store: CounterStore): void {
```ts
// counter.store.ts
-export const CounterStore = signalStore(withState({ count: 0 }));
+export const CounterStore = signalStore(withState({count: 0}));
export type CounterStore = InstanceType;
// counter.component.ts
-import { CounterStore } from './counter.store';
+import {CounterStore} from './counter.store';
-@Component({ /* ... */ })
+@Component({ /* ... */})
export class CounterComponent {
- constructor(readonly store: CounterStore) {}
+ constructor(readonly store: CounterStore) {
+ }
}
```
+
+
+
+
+I get the error "Cannot assign to read only property 'X' of object '[object Object]'"
+
+The state in the SignalStore must be immutable. If you make mutable changes, thereโs a high risk of introducing subtle, hard-to-diagnose bugs. To protect against this, SignalStore introduced an additional check to enforce immutability.
+
+The immutability requirement originates from Angular's Signal itself, which serves as the foundation of the SignalStore. Hereโs an example to illustrate this:
+
+```ts
+const person = signal({name: 'Konrad', age: 25});
+const personFormat = computed(
+ () => `${person().name} is ${person().age} years old`,
+);
+
+console.log(personFormat()); // shows 25 years
+
+person().age = 30; // ๐ mutable change
+console.log(personFormat()); // ๐ person did not notify personFormat. still 25 years
+
+// another mutable change
+person.update((value) => {
+ value.age++; // ๐
+ return value;
+});
+
+console.log(personFormat()); // ๐ no notification. 25 years.
+
+// immutable change
+person.update((value) => ({
+ ...value, // ๐ immutable change
+ counter: 40,
+}));
+console.log(personFormat()); // ๐ personFormat has been notified and shows 40 years.
+
+```
+
+As you can see, the problem typically arises in computed, effect or the component's template, not directly at the root (the signal mutation itself). This is why these issues are so hard to debug.
+
+You might look into your components wondering why theyโre not updating, while the real error is buried deep within the SignalStore.
+
+Therefore, both `signalState` and `signalStore` throw on those mutable changes. They protect you!
+
+```typescript
+const person = signalState({name: 'Konrad', age: 25});
+patchState(person, (value) => {
+ value.age++
+ return value;
+}) // ๐ฅ throws
+
+
+person().age = 30; // ๐ฅ throws
+```
+
+If you require mutable properties in your state, then put them into `withProps`.
+
+```ts
+const PersonStore = signalStore(
+ withProps({name: 'Konrad', age: 25}),
+ withMethods(store => ({
+ setAge(age: number) {
+ store.age = age;
+ }
+ })));
+const personStore = new PersonStore();
+
+personStore.setAge(30);
+```
+