更新ngrx/store中的对象

Dan*_*ick 7 javascript rxjs typescript ngrx angular

我正在使用@ ngrx/store来购买Angular 2应用程序.

我的商店里有一个说明Book对象的清单.我想更新其中一个对象中的字段.我也碰巧有一个我想要更新的Book实例的Observable(比方说selectedBook).

为了进行更新,我打算使用UpdateBookAction新书的一个和一个有效负载调用reducer .所以我通过订阅selectedBook然后调用Object.assign()来制作现有Book对象的深层副本.

但是当我尝试写入副本的一个字段时,我得到以下错误.(如果我试图直接写入商店中的Book对象,那么碰巧是我得到的错误.)

错误

Cannot assign to read only property 'name' of object '#<Object>' at ViewWrappedError.BaseError [as constructor]
Run Code Online (Sandbox Code Playgroud)

ngOnInit() {
    this.book$ = this.store.let(fromRoot.getSelectedBook);
    //...
}

someFunction() {
    //...
    this.book$.subscribe(book => {

        let updatedBook = Object.assign({}, book);
        updatedBook.name = 'something else';          // <--- THIS IS WHAT THROWS

        let action = new BookUpdateAction(updatedBook);
        this.store.dispatch(action);

    }
}
Run Code Online (Sandbox Code Playgroud)

评论后澄清

我假设我可能有一个有效载荷的动作,而不是整个商店的状态.(事实上​​这似乎是必要的,不是吗?)我相信这是文件的情况.

我想采取的行动是这样的:

Action = UPDATE, payload = {'id': 1234, 'name': 'something new'}
Run Code Online (Sandbox Code Playgroud)

如上所述,我打算像这样打电话:

this.store.dispatch(action);
Run Code Online (Sandbox Code Playgroud)

据推测,ngrx将我的动作与(不可变的)当前状态一起传递给减速器.

所以从那里,一切都应该工作正常.我在reducer中的逻辑不会改变现有状态,它只是从现有状态和我传入的有效负载中创建一个新状态.

这里真正的问题是如何合理地构建新的"objectToUpdate",以便我可以将其作为有效负载传递.

我可以这样做:

this.book$.subscribe(book => {

    let updatedBook = new Book();
    updatedBook.id = book.id;
    //set all other fields manually...
    updatedBook.name = 'something else';

    let action = new BookUpdateAction(updatedBook);
    this.store.dispatch(action);

}
Run Code Online (Sandbox Code Playgroud)

但我们不只是在这里谈论两个领域......如果我的书有几个领域怎么办?每次只更新一个字段时,是否必须从头开始手动构建新书?

我的解决方案是使用Object.assign({}, book)(而不是改变旧版本!)进行深层复制,然后将更新仅用于我想要触摸的字段.

Den*_*ski 8

ngrx商店的想法是拥有一个且只有一个真实的地方,这意味着所有对象都是不可变的,改变任何东西的唯一方法就是重新创建整个事物.此外,您可能正在使用ngrx冻结(https://github.com/codewareio/ngrx-store-freeze),这意味着所有对象都将以只读方式创建,因此您无法更改任何对象(这是如果你想完全遵循redux模式,那么有利于开发).如果您删除商店冻结对象的部分,您将能够更改它,但这不是最佳实践.

我建议您使用以下内容:使用带有异步管道的ngrx observable将数据(在您的案例中)放入一个只能输入和输出某个事件的哑组件中.在哑组件内部,您可以通过复制它来"编辑"该对象,并在完成后,您可以将更改发回到订阅商店的智能组件并允许其更改状态通过商店(提交).这种方式是最好的,因为对于一个非常小的更改来改变整个状态并不常见(比如双向绑定,当用户输入时......).

如果您遵循redux模式,那么您将能够添加历史记录,这意味着商店将保留最后X个状态重新创建的副本,因此您可以获得UNDO功能,更易于调试,时间线等

您的问题是您正在直接编辑属性而不是重新创建整个状态.


And*_*ang 6

我将不得不对OP正在经历的实际情况做出假设.

问题

无法修改冻结对象的成员.它的错误被抛出.

原因

ngrx-store-freeze用作元缩减器来冻结进入商店的任何对象.在另一个地方,当需要更改对象时,正在进行浅拷贝.Object.assign()不做深拷贝.正在修改从原始对象到达的另一个对象的成员.此次要对象也被冻结,因为它不会重复.

使用像cloneDeep()lodash 这样的深层副本.或者通过适当的行动发送一袋待更改的房产.处理减速器上的更改.