【问题标题】:React setState not merging all changes反应 setState 不合并所有更改
【发布时间】:2020-05-27 23:02:21
【问题描述】:

编辑 - 我目前无法为自己的答案投票,但事实证明这是一个参考/不变性问题 - 请参阅 https://stackoverflow.com/a/60189228/11602055

这个让我彻底崩溃了!

我正在从我的服务器获取一些数据并将其应用到我的状态,但是我应用到状态的一些属性在 setState 完成后不会迁移到状态...

我已经尝试了我能想到的一切,我正在读取最终状态作为 setState 回调的一部分,以确保我在 setState 完成后获得状态,因此与 setState 异步无关在自然界中。

尝试了以下 -

  • 使用空白对象,将新道具保存到该对象,然后将其返回。
  • 在键的外部循环内的单独 setState 调用中一次应用一个属性
  • 返回 Object.assign({},this.state,data);
  • 返回 {...data};
  • 返回 {...this.state,...data};
  • 等待 setState(...);
  • setState(数据)

我有几个嵌套的状态属性,但这会影响状态对象的根属性...

请参阅下面的代码/输出/结束状态,例如,请注意 eventColour 尚未通过 setState 操作传播到最终状态...(同样适用于 iconCls...)

请帮忙?...

代码:

getJobDetail = async function (){

    // get data from server
    let response = await window.fetch("/event/events?id="+this.props.job,{cache: "no-cache", credentials: "same-origin"});

    // parse returned data
    let data = await response.json();

    // couple of minor data manipulation exercises
    if (data.tempToFeedback!==null){
        data.toFeedback=data.tempToFeedback;
    }
    if (data.tempSwitchNotes!==null){
        data.switchingNotes=data.tempSwitchNotes;
    }
    if (data.editedName!==null){
        data.name=data.editedName;
    }
    if (data.fileList!==null){
        data.fileList=JSON.parse(data.fileList);
    }

    // merge all recieved data onto state
    this.setState(currentState=>{

        for (let key of Object.keys(data)){
            console.log("setting " + key + " from "+currentState[key]+" to " + data[key]);
            currentState[key]=data[key];
        }

        return currentState;
    }
    ,
        ()=>{
            // output new state on callback (doesn't include everything which was merged in...)
            console.log(JSON.parse(JSON.stringify(this.state)))
        }
    )

}

setState 期间的控制台输出

setting id from undefined to 214793


setting tnccJobsID from undefined to 214793
 setting startDate from undefined to 2020-02-12T08:00:00.000Z
 setting endDate from undefined to 2020-02-12T09:00:00.000Z
 setting resourceId from undefined to 99921
 setting FKusers from undefined to 99999
 setting previousJob from undefined to null
 setting isInfoBooking from undefined to 0
 setting isCSP from undefined to null
 setting isDAR from undefined to 0
 setting hasStarted from undefined to 0
 setting hasEnded from undefined to 0
 setting isCancelled from undefined to 0
 setting FKtogaGroups from undefined to 12327
 setting currentStep from undefined to 0
 setting adjustedFKjobTypes from undefined to null
 setting FKjobTypes from undefined to 23
 setting lastUpdated from undefined to null
 setting name from undefined to undefined
 setting jobTypeID from undefined to 23
 setting eventType from undefined to Operational Release
 setting eventColor from undefined to orange
 setting eventStyle from undefined to colored
 setting iconCls from undefined to fas fa-plane-departure
 setting weight from undefined to 1
 setting northDesk from undefined to 1
 setting southDesk from undefined to 0
 setting switchOutState from undefined to 1
 setting feedbackState from undefined to 1
 setting feedbackRequired from undefined to 0
 setting demandAtRisk from undefined to null
 setting demandAtRiskApproved from undefined to null
 setting runwaySteps from undefined to [object Object],[object Object],[object Object],[object Object],[object Object]
 setting startStep from undefined to 4
 setting endStep from undefined to 5
 setting fileList from undefined to 
 setting outageList from undefined to [object Object]
 setting toFeedback from undefined to undefined
 setting switchingNotes from undefined to undefined

回调中读出的最终状态

updateData: {}
fileDrawer: null
fileUploading: null
showJobDetailModal: false
id: 214793
tnccJobsID: 214793
startDate: "2020-02-12T08:00:00.000Z"
endDate: "2020-02-12T09:00:00.000Z"
resourceId: 99921
FKusers: 99999
previousJob: null
isInfoBooking: 0
isCSP: null
isDAR: 0
hasStarted: 0
hasEnded: 0
isCancelled: 0
FKtogaGroups: 12327
currentStep: 0
adjustedFKjobTypes: null
FKjobTypes: 23
lastUpdated: null
jobTypeID: 23
eventType: "Operational Release"
eventStyle: "colored"
weight: 1
northDesk: 1
southDesk: 0
switchOutState: 1
feedbackState: 1
feedbackRequired: 0
demandAtRisk: null
demandAtRiskApproved: null
runwaySteps: (5) [{…}, {…}, {…}, {…}, {…}]
startStep: 4
endStep: 5
fileList: []
outageList: [{…}]
__proto__: Object

图像格式也是如此!

Code

setState Output

state as read in callback

包含数据的代码演示版:

    import React, { Component } from 'react';


export const JobView = class JobView extends React.PureComponent{



    render(){


            return null;

    }

}


export const JobDetails = class JobDetails extends React.Component{
    constructor(props){
        super(props);
        this.state={
            // jobData:null,
            updateData:{},


        }


        this.getJobDetail = this.getJobDetail.bind(this);

        this.setState = this.setState.bind(this);


    }

    componentDidMount(){





        this.getJobDetail();


        }


    getJobDetail = async function (){

        // get data from server
        // let response = await window.fetch("/event/events?id="+this.props.job,{cache: "no-cache", credentials: "same-origin"});

        // parse returned data
        // let data = await response.json();
        let data = JSON.parse('{"id":5267,"tnccJobsID":5267,"startDate":"2020-02-12T08:00:00.000Z","endDate":"2020-02-12T09:00:00.000Z","resourceId":99921,"FKusers":99999,"previousJob":null,"isInfoBooking":0,"isCSP":null,"isDAR":0,"hasStarted":null,"hasEnded":null,"isCancelled":null,"FKtogaGroups":2830,"currentStep":1,"adjustedFKjobTypes":null,"FKjobTypes":1,"lastUpdated":"2020-01-21T22:30:43.000Z","name":"OCKER HILL 132KV BUS SECTION 120 .","jobTypeID":1,"eventType":"Operational Release","eventColor":"orange","eventStyle":"colored","iconCls":"fas fa-plane-departure","weight":1,"northDesk":1,"southDesk":0,"switchOutState":1,"feedbackState":1,"feedbackRequired":0,"demandAtRisk":null,"demandAtRiskApproved":null,"runwaySteps":[{"step":1,"active":{"icon":"loading","text":"Planning"},"isLast":false,"completed":{"icon":"bars","text":"Planned"}},{"step":2,"active":{"icon":"loading","text":"Negotiating TSC"},"isLast":false,"completed":{"icon":"file-done","text":"TSC Recieved"}},{"step":3,"active":{"icon":"loading","text":"Awaiting Allocation"},"isLast":false,"completed":{"icon":"user","text":"Allocated"}},{"step":4,"active":{"icon":"loading","text":"Waiting to Start"},"isLast":false,"completed":{"icon":"user","text":"Started"}},{"step":5,"active":{"icon":"loading","text":"Switching In Progress"},"isLast":false,"completed":{"icon":"api","text":"Switched"}},{"step":6,"active":{"icon":"loading","text":"Releasing To Safety"},"isLast":true,"completed":{"icon":"check","text":"Released To Safety"}}],"startStep":4,"endStep":6,"fullHistory":[{"time":"2019-11-23T22:58:44.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"hasEnded Changed"},{"time":"2019-11-23T22:58:44.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"hasStarted Changed"},{"time":"2019-11-23T22:58:44.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"isCancelled Changed"},{"time":"2019-11-25T23:03:12.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"hasEnded Changed"},{"time":"2019-11-25T23:03:12.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"hasStarted Changed"},{"time":"2019-11-25T23:03:12.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"isCancelled Changed"},{"time":"2019-11-28T22:59:03.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"hasEnded Changed"},{"time":"2019-11-28T22:59:03.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"hasStarted Changed"},{"time":"2019-11-28T22:59:03.000Z","entryTo":"No","entryFrom":0,"changedBy":"System","message":"isCancelled Changed"},{"time":"2020-01-13T22:39:10.000Z","entryTo":0,"entryFrom":null,"changedBy":"System","message":"isDAR Changed"}],"fileList":"[]","outageList":[]}');

        // console.log(JSON.stringify(data));
        // couple of minor data manipulation exercises
        if (data.tempToFeedback!==null){
            data.toFeedback=data.tempToFeedback;
        }
        if (data.tempSwitchNotes!==null){
            data.switchingNotes=data.tempSwitchNotes;
        }
        if (data.editedName!==null){
            data.name=data.editedName;
        }
        if (data.fileList!==null){
            data.fileList=JSON.parse(data.fileList);
        }

        // merge all recieved data onto state
        await this.setState((currentState,props)=>{
            let newState = {}
            for (let key of Object.keys(data)){
                console.log("setting " + key + " from "+currentState[key]+" to " + data[key]);
                newState[key]=data[key];
            }
            console.log(newState);
            return newState;
        }


        ,
            ()=>{
                // output new state on callback (doesn't include everything which was merged in...)

                console.log(this.state);
            }
        )

    }





    render(){
        return JSON.stringify(this.state);
    }

}

【问题讨论】:

  • 欢迎来到 SO,请将代码添加到您的问题中,而不是图像中。如果图像消失,这个问题就变得毫无意义。
  • 完成,抱歉:)

标签: javascript reactjs


【解决方案1】:

原来我是愚蠢的!但是对于遇到这个问题的其他人,我的渲染函数的一部分是这样的:

constructor(props){
  super(props);
  this.state={prop1:"first value"};
}
render(){
  console.log(JSON.parse(JSON.stringify(this.state.prop1))) // "first value"
  let easyToAccessState = this.state;
  easyToAccessState.prop1 = "another value";
  console.log(JSON.parse(JSON.stringify(this.state.prop1))) // "another value"
}

为避免这种情况,请在 easyToAccessState 变量中创建一份状态副本:

render(){
  console.log(JSON.parse(JSON.stringify(this.state.prop1))) // "first value"
  let easyToAccessState = {...this.state}; // COPIES STATE
  easyToAccessState.prop1 = "another value";
  console.log(JSON.parse(JSON.stringify(this.state.prop1))) // "first value"
}

显然理想情况下,在渲染过程中不应该对任何内容进行任何更改...这是我要从中删除的内容,并将移动 prop1 的重新计算以使其正确进入状态并正确使用它第一名……

这些东西是用来测试我们的……不变性和引用啊!

【讨论】:

    【解决方案2】:

    setState尝试返回{ ...currentState }

    【讨论】:

      【解决方案3】:

      尝试这样设置状态

          this.setState({...this.state, ...data},
              () => {
                  // output new state on callback (doesn't include everything which was merged in...)
                  console.log(JSON.parse(JSON.stringify(this.state)))
              }
          )
      

      【讨论】:

      • 同样的问题,没有变化:(
      【解决方案4】:

      试试这个:

      this.setState((currentState, props)=>{
          let newState = {};
              for (let key of Object.keys(data)){
                  newState[key] = data[key];
                  console.log("setting " + key + " from "+currentState[key]+" to " + data[key]);
              }
      
          return newState;
      }
      

      【讨论】:

      • 不幸的是仍然得到相同的结果:(
      • 将 await 放在 setState 之前,然后在控制台检查您是否在异步完成之前打印它。
      • 刚刚尝试过自己认为也许我是一个完整的eejit......但并没有改变结果:(
      • 很好,它应该可以这样工作,但我认为工作代码环境将帮助这里的人们以更有效的方式帮助您。否则这种预测游戏会一直持续下去,直到击中靶心。
      • 能否请您在这里给出“响应”对象,让我在我的环境中尝试。
      【解决方案5】:

      您无需将currentState 与新状态合并 - 默认情况下,状态会将新值合并到旧值中

      你试过简单吗:

      this.setState(data);
      
      // Instead of
      // merge all received data onto state
      // await this.setState((currentState,props)=>{
      //       let newState = {}
      //       for (let key of Object.keys(data)){
      //           console.log("setting " + key + " from "+currentState[key]+" to " + data[key]);
      //           newState[key]=data[key];
      //       }
      //       console.log(newState);
      //       return newState;
      //   }
      
      

      你正在尝试做一些 React 的工作。

      另外,我认为您不需要在this.setState 前面加上await。虽然setStateasynchronous“不要依赖this.state 在调用setState 后立即反映新值” - details here

      希望对你有帮助!

      【讨论】:

      • 刚试过这个,以为我在一切都出错的时候回到了这里!不幸的是,目前结果相同......
      【解决方案6】:

      你应该试试这个this.setState(data,()=>console.log(this.state))

      查看工作代码codesandbox link

      如果您的初始状态在收到响应后定义为空,它也可以设置状态您的数据

      【讨论】:

        猜你喜欢
        • 2021-03-23
        • 2022-07-06
        • 2021-09-10
        • 2021-01-28
        相关资源
        最近更新 更多