【问题标题】:React - redux state mutation errorReact - redux 状态突变错误
【发布时间】:2017-10-15 20:25:03
【问题描述】:

我正在尝试将部分添加到调查对象及其引发状态突变错误。

这是我调用将整个调查对象作为参数的动作创建者的方法。

   addNewSection(sectionName){
   const id = performance.now();
    let newSurvey = Object.assign({}, this.state.survey);
    const section = Object.assign({},{
        "type": "section",
        "id": id,
        "title": sectionName,
        "position": {
            "offset": 0,
            "width": 12,
            "order": 1
        },
        "items": []
    });
    newSurvey.layout.sections.push(section);
    this.setState({survey: newSurvey});
    this.props.actions.updateSurvey(newSurvey);
}

动作创建者:

export function updateSurvey(survey){
return {type:types.ADD_SECTION_SUCCESS, survey};
}

减速器:

export default function surveyReducer(state = initialState.survey, action){
switch(action.type){

    case types.ADD_SECTION_SUCCESS:
        return action.survey;

    default:
         return state
    }    
}

状态对象的形式为:

survey: {
layout: {
    sections: [{},{},{}]
    },
    questions:[{},{},{}]
}

我一定是误解了 Object.assign。 Object.assign 是否会复制调查中的每个嵌套对象,如果我只是在调查对象的最顶层使用它,就像我在这里使用的一样?

【问题讨论】:

    标签: javascript reactjs redux frontend mutation


    【解决方案1】:

    首先,上面提到的解决方案是完全错误的。 Object.assign 和 ES6 扩展运算符永远不会在像您这样的深度嵌套的数据结构上工作。它们不会阻止任何状态突变。

    其次,状态突变错误总是与 REDUX 的状态相关,而不是本地状态。

    export default function headerReducer(state = {userName: ''}, action) {
        switch (action.type) {
            case 'SSO_USER_ACTION':
            {
                return Object.assign({}, state, { userName: action.payload });
            }
            default:
                return state;
        }
    }
    

    查看上面的示例减速器。在这里,我们总是使用以下方法返回一个新对象:

    return Object.assign({}, state, { userName: action.payload });
    

    但是,在您的情况下,状态对象“调查”并不是那么简单。它是深度嵌套的,Object.assign OR 扩展运算符根本无助于防止突变。您有 3 个选项:

    1. 使用不可变的 JS 库,该库提供永远不会改变的地图和列表。
    2. 使用规范化库来扁平化您的状态。
    3. (一种不需要额外库的脏方法)使用以下解决方案修复突变。

    【讨论】:

      【解决方案2】:

      看起来你对应该在哪里改变状态有点困惑。看起来您在 addNewSection 函数中根本没有使用 redux 状态。您只是在更新本地组件状态。你甚至不需要在这里使用 object.assign ,你可以更新本地状态并随心所欲地改变它。

      当您将新调查作为调查状态返回时,您在减速器中改变了您的状态。

      您需要使用 connect 和 mapStateToProps() 从道具访问您的调查状态。然后你可以在你的 reducer 中使用你的 object.assign 逻辑来返回一个新的状态副本。

      function mapStateToProps(state) {
        return { survey: state.survey }
      }
      

      您可以从 this.props.survey 访问调查并将其显示在您想要的位置。

      您的 addNewSection() 函数中根本不需要 object.assign。只需将您的新部分变量,将其传递给您的动作创建者,您的动作创建者会将其分派给您的减速器,您将使用 object.assign 返回一个新的状态副本,该副本将在您的 this.props.survey 上更新。

      另外,建议在 object.assign 上使用对象扩展语法以获得更好的语法,请参见此处:http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html

      这里是关于使用 mapStateToProps 的提示: https://github.com/reactjs/react-redux/blob/master/docs/api.md

      【讨论】:

      • 我 100% 同意你的观点,而且我曾经这样做过。但我的领导建议这样做……顺便说一句,他也是新来的。所以我用了这个方法。假设我按照你说的做,在减速器中,我是否必须这样做: return Object.assign({},survey,{ layout: Object.assign({},survey.layout,{ items: [.. .survey.layout.items, 部分] }) });就是这样:返回 Object.assign({},survey} 换句话说,我必须复制调查中的每个对象还是只复制调查。
      • 我总是这样做,而且很容易理解。它看起来像: { ...state, survey: action.survey } 这等于: Object.assign({}, state, {survey: action.survey }) 基本上你正在替换你的旧状态,但不保留同一个对象。 Object.assign() 返回一个新对象。
      • 编辑:抱歉没有正确阅读问题。不,您不需要使用嵌套的 Object.assigns。只需使用一个 Object.assign() 将状态复制到一个新对象中。 Object.assign() 将状态复制到空对象中,然后将第三个参数复制到顶部。因此它不会改变任何东西,只是用那个空对象制作一个副本!
      • 这不是我在组件中所做的吗?我使用了 Object.assign({},survey) 来制作调查的副本,然后我将该部分推入其中,然后将整个调查对象发送到 this.props.actions.updateSurvey(newSurvey) 中的 redux;陈述。我错过了什么吗?当我这样做时,我得到了突变错误。如果我这样分配对象: let newSurvey = Object.assign({},survey,{ layout: Object.assign({},survey.layout,{ items: [...survey.layout.items, section] }) });它有效。
      • @vineeth 抱歉,我有点误解了。你正在改变你的组件状态,这是你得到错误的地方。 Object.assign 不会进行深度克隆。它只是复制对象属性值。当您将新部分推送到 newSurvey 时,它实际上也会改变 this.state.survey,因为嵌套对象只是引用。这一切都与redux无关。您实际上是在这里收到错误还是只是警告?
      【解决方案3】:

      您可以使用扩展运算符以 ES6 方式克隆对象:

      let newSurvey = {...this.state.survey}
      

      【讨论】:

        猜你喜欢
        • 2019-12-25
        • 2017-09-26
        • 2019-02-01
        • 2017-04-24
        • 2019-03-16
        • 1970-01-01
        • 1970-01-01
        • 2018-10-03
        • 1970-01-01
        相关资源
        最近更新 更多