I\'m using @ngrx/store for an Angular 2 app.
My store holds a list of say, Book
objects. I want to update a field in one of those objects. I also happ
As already mentioned - the reason you are getting
Cannot assign to read only property 'name' of object
is because 'ngrx-store-freeze' freezes the state and prevents mutating it.
Object.assign will provide a new object as you expect, but it will copy the state's properties along with each property's own definition - such as the 'writable' definition (which 'ngrx-store-freeze' likely sets to false).
A different approach is described in this answer and explains how cloning objects with JSON.parse(JSON.stringify(yourObject)) as fastest, but this approach has flaws if you keep dates or methods etc' in your state.
using lodash's 'cloneDeep' is probably your best bet for deep cloning the state.
The idea of the ngrx store is to have one and only one single place of truth, which means all the objects are immutable, and the only way to change anything is to recreate everything as a whole. Also, you are probably using the ngrx freeze (https://github.com/codewareio/ngrx-store-freeze) which means that all of the objects will be created read-only so you wont be able to change any (This is good for development if you want to completely follow the redux pattern). If you remove the part where the store freezes the object, you will be able to change it, but thats not best practice.
What I would suggest you is the following: Use the ngrx observable with async pipe to put the data (in your case books) in a dumb component which can only get input and output some event. Than, inside of the dumb component you can "edit" that object by making a copy of it, and after you are done, you can emit back the changes to the smart component which is subscribed to the store and allow it to change the state via the store (commit). This way is best because it is not very common to change the whole state for a really small change (like two way binding, when user types..).
If you follow the redux pattern, than you will be able to add history, which means the store will keep a copies of the last X state recreations, so you can get UNDO functionality, easier to debug, timeline etc
Your problem is that you are directly editing the property instead of recreating the whole state.
I'll have to make an assumption about the actual scenario the OP is experiencing.
The problem
It's not possible to modify a member of a frozen object. Its the error being thrown.
The cause
ngrx-store-freeze
is used as a meta-reducer to freeze any object that enters the store. On another place, when an object needs to be changed, a shallow copy is being made. Object.assign()
doesn't do deep copy. A member of another object reached from the original object is being modified. This secondary object is also frozen, by it is not duplicated.
Solution
Use a deep copy like cloneDeep()
from lodash. Or sent a bag of properties to be changed with a proper action. Process the changes on the reducer.
One way to accomplish this is a utility/helper method to make a new book from.
You could give it an existing book and the subset of properties you want to add to a new book (using Partial
in typeScript
if you want type safety).
createNewBook(oldBook: Book, newProps: Partial<Book>): Book {
const newBook = new Book();
for(const prop in oldBook) {
if(newProps[prop]) {
newBook[prop]=newProps[prop];
} else {
newBook[prop]=oldBook[prop];
}
}
return newBook
}
You could call it via newBook = createNewBook(new Book(), {title: 'first foo, then bar'});
and use this newBook to update your store.