【问题标题】:What does ...state do exactly? [duplicate]...state 究竟做了什么? [复制]
【发布时间】:2019-03-06 23:17:34
【问题描述】:

我正在 Angular 中尝试 NgRx (redux) 和下面示例中的 ...state,我无法完全理解。我搜索了一下,一般理解为spread,但不知道为什么Interface State中的数据属性在reducer的switchreturn{}块中重复,因为...会spread em> 他们呢?谁能帮我理解一下?

export interface State {
  sessionData: Map<string, string>;
  requesting: boolean;
  hasError: boolean;
  status: StatusModel;
}

export function sessionReducer(state: State = INITIAL_STATE, action: Session.Actions): State {
      switch (action.type) {
        case Session.REQUEST_SESSION_DATA:
          return {
            ...state,
            requesting: true,
            hasError: false,
            status: undefined,
          };
      }
}

PS:我查看了线程here 并且通常认为传播正是这样做的,传播了。但这里在 Redux/NgRx 的上下文中,试图理解为什么 return{}...state 和三个附加属性。

【问题讨论】:

  • 听起来应该只包含sessionData,而不是...state,假设State 只包含这些属性而没有其他属性

标签: javascript angular ngrx-store


【解决方案1】:

状态的关键在于它是不可变的(返回一个新对象,而不是修改后的对象)。所以,如果你想修改状态添加新值,你需要返回当前状态加上你想添加到前一个状态的新值。在使用扩展运算符... 的示例中,您将返回一个新的不可变对象,其中包含先前的状态以及三个新属性requestinghasErrorstatus。你可以考虑这样做:

export function sessionReducer(state: State = INITIAL_STATE, action: Session.Actions): State {
      switch (action.type) {
        case Session.REQUEST_SESSION_DATA:
          state.requesting = true;
          state.hasError: false;
          state.status: undefined;

          return state;
      }
}

但是你不能这样做,因为你打破了状态哲学,新的不可变对象而不是修改过的对象:)

在您的示例中,我们需要知道INITIAL_STATE 是如何初始化的,但我认为它只包含sessionData 属性。因此,在该示例中,您将返回 sessionData 以及其余属性。

在下面的链接中,您可以看到扩展运算符是 Redux 世界中常用的运算符,用于将当前状态作为新对象返回(它是 React 的 Redux 示例,但它在 Angular 中的工作方式完全相同)。

在 Angular 中,使用 Redux 模式和 OnPush 更改检测策略是一种非常常见的模式,因为您告诉 Angular 仅检查组件 @Input 中的参考更改,而不是逐个比较对象比较属性.这是性能上的一大优势。

Use of Spread Operator with Redux

【讨论】:

  • 谢谢,我会在该链接上阅读更多内容。
【解决方案2】:

... 被称为spread operator

扩展运算符“解包”该对象中的所有内容。

return {
    ...state,
    requesting: true,
    hasError: false,
    status: undefined,
};

相同
return {
    sessionData: state.sessionData,
    requesting: state.requesting,
    hasError: state.hasError
    status: state.status,
    requesting: true,
    hasError: false,
    status: undefined,
};

【讨论】:

  • 好的,正如@CertainPerformance 注释掉的那样,返回应该只是'{...state}'?
【解决方案3】:

您是正确的,这是spread operator。基本上,它提取给定对象的所有属性。另一种写法是:

let newState = {
  sessionData: state.sessionData,
  requesting: state.requesting,
  hasError: state.hasError,
  status: state.status
};
newState.requesting = true;
newState.hasError = true;
newState.status = undefined;
return newState;

扩展运算符让您无需了解所有属性被调用,并确保您未更改的所有值都按原样传递。

换一种说法

{ ...state }

制作state 的浅拷贝。然后,您可以随意修改该浅拷贝。

【讨论】:

  • 当您实际上只想要该对象的一个​​属性时,传播一个对象似乎毫无意义,我想? (我不太明白为什么原来的代码是用spread)
  • @CertainPerformance 正是我的困惑。因此,我认为我对...有误解或理解上的差距|
  • @Vivek 它可以保护您免受状态形状的更新。如果您决定稍后将更多属性添加到状态,则不必记住在复制特定属性的所有位置进行更新。它还可以减少打字。
【解决方案4】:

这就是说返回一个具有所有状态属性的新对象并添加/修改 3 个附加属性。

值得知道{...state} 深度等于state,这样:

_.isEqual(state, {...state}) // returns true

但是:

{...state} !== state

这是因为{} 创建了一个新对象,而扩展运算符只是将道具添加到该对象。因此,状态的对象引用与新的对象引用是不一样的。这就是变更检测通常的工作方式。它查看对象引用是否已更改。如果有,它就知道有变化并采取相应的行动。使用这种方法的原因是因为它比检查深度相等要快得多,因为它必须检查所有属性。

Reducer 经常使用扩展运算符,他们只想修改状态的属性之一。如果他们这样做:

state.requesting = true
return state

那么在这个例子中,对象引用并没有改变,因此变化检测没有启动,你被塞满了。

为了强制执行变更检测,有一个很棒的小库ngrx-store-freeze,它可以防止您意外改变状态,这会导致许多难以重现的错误。这个库实际上是 Udemy 在其付费的 ngrx 课程中推荐的。

【讨论】:

    猜你喜欢
    • 2019-02-15
    • 1970-01-01
    • 1970-01-01
    • 2015-09-15
    • 2012-07-23
    • 2016-09-10
    • 2023-03-15
    • 2012-10-17
    • 2021-06-04
    相关资源
    最近更新 更多