反应SetState覆盖整个对象而不是合并

如何解决反应SetState覆盖整个对象而不是合并

我的状态在构造函数中如下所示:

this.state = {
      selectedFile: null,//current file selected for upload.
      appStatus: 'waiting for zip...',//status view
      zipUploaded: false,zipUnpacked: false,capturingScreens: false,finishedCapture: false,htmlFiles: null,generatedList: [],optionValues: { 
        delayValue: 1
      },sessionId: null,estimatedTime: null,zippedBackupFile: null,secondsElapsed:0,timer: {
        screenshotStart:0,screenshotEnd:0,timingArray:[],averageTimePerUnit:25,totalEstimate:0
      }
    };

我的app.js中具有以下功能:

this.secondsCounter = setInterval(this.countSeconds,1000); // set inside the constructor


getStateCopy = () => Object.assign({},this.state);

countSeconds = () => {
    let stateCopy = this.getStateCopy();
   
    let currentSeconds = stateCopy.secondsElapsed + 1;

    this.setState({secondsElapsed:currentSeconds});
  }
  captureTime = (startOrStop) => {
    let stateCopy = this.getStateCopy();
    let secondsCopy = stateCopy.secondsElapsed;
    let startPoint;

    if(startOrStop === true) {
      this.setState({timer:{screenshotStart:secondsCopy}});
    } else if(startOrStop === false){
      this.setState({timer:{screenshotEnd:secondsCopy}});
      startPoint = stateCopy.timer.screenshotStart;
      stateCopy.timer.timingArray.push((secondsCopy-startPoint));
      this.setState({secondsElapsed:secondsCopy})
      
      stateCopy.timer.averageTimePerUnit = stateCopy.timer.timingArray.reduce((a,b) => a + b,0) / stateCopy.timer.timingArray.length;
      this.setState({secondsElapsed:secondsCopy})
      this.setState({timer:{averageTimePerUnit:stateCopy.timer.averageTimePerUnit}})
  
    }

我收到一个错误,指出stateCopy.timer.timingArray上不存在“ push”。我进行了一些调查,发现this.setState({timer:{screenshotStart:secondsCopy}});实际上是覆盖状态中的整个“计时器”对象,并删除所有先前的属性,而不是合并它们。

我不明白我在做什么错..我正在使用stateCopy来避免对状态进行变异,并获取适当的值(避免异步混淆)。我在网上阅读的每一篇关于react的文章都表明,编写一个要声明状态的对象将与已经存在的对象合并,所以为什么它会不断覆盖“ timer”而不是合并?

解决方法

我做了一些调查,发现this.setState({timer:{screenshotStart:secondsCopy}});实际上是覆盖状态中的整个“计时器”对象,并删除所有先前的属性,而不是合并它们。

正确。 setState仅在顶级处进行合并。低于此的任何东西都必须处理。例如:

this.setState(({timer}) => {timer: {...timer,screenshotStart: secondsCopy}});

请注意使用setState的回调版本。重要的是,必须在任何时间内提供依赖于现有状态的状态信息。

在其他地方,您也必须执行相同的操作,包括当您推送到数组时。以下是一些其他说明:

没有理由在此处复制状态:

countSeconds = () => {
    let stateCopy = this.getStateCopy();
    let currentSeconds = stateCopy.secondsElapsed + 1;
    this.setState({secondsElapsed: currentSeconds});
}

...并且(如上所述),您必须使用回调形式来基于现有状态可靠地修改状态。相反:

countSeconds = () => {
    this.setState(({secondsElapsed}) => {secondsElapsed: secondsElapsed + 1});
};

类似地在captureTime中:

captureTime = (startOrStop) => {
    if (startOrStop) { // *** There's no reason for `=== true`
        this.setState(({timer,secondsElapsed}) => {timer: {...timer,screenshotStart: secondsElapsed}});
    } else { // *** Unless `startOrStop` may be missing or something,no need for `if` or `=== false`.
        this.setState(({timer,secondsElapsed}) => {
            const timingArray = [...timer.timingArray,secondsElapsed - timer.screenshotStart];
            const update = {
                timer: {
                    ...timer,screenshotEnd: secondsElapsed,timingArray,averageTimePerUnit: timingArray.reduce((a,b) => a + b,0)
                }
            };
        });
    }
};

旁注:您的copyState函数执行状态复制。因此,如果您修改其包含的对象的任何属性,那么您将直接修改状态,而在React中则不必这样做。

,

setState挂钩总是用一个新对象覆盖状态……这是它们的正确行为。

您需要在setState中使用一个函数。不只是传递一个对象。

setState((prevState,prevProps)=>{
//logic to make a new object that you will return ... copy properties from prevState as needed.
 //something like const newState = {...prevState} //iffy myself on exact syntax

return newState

})
,

您的getStateCopy仅克隆现有状态-嵌套的任何内容都不会克隆。为了说明:

const getStateCopy = () => Object.assign({},state);
const state = {
  foo: 'bar',arr: [1,2]
};

const shallowCopy = getStateCopy();
shallowCopy.foo = 'newFoo';
shallowCopy.arr.push(3);
console.log(state);

或者先深度克隆状态,或者使用spread添加所需的新属性:

countSeconds = () => {
    this.setState({
        ...this.state,secondsElapsed: this.state.secondsElapsed + 1
    });
}
captureTime = (startOrStop) => {
    if (startOrStop === true) {
        this.setState({ ...this.state,timer: { ...this.timer,screenshotStart: this.state.secondsElapsed } });
    } else if (startOrStop === false) {
        const newTimingValue = this.state.secondsElapsed - this.state.timer.screenshotStart;
        const newTimingArray = [...this.state.timer.timingArray,newTimingValue];
        this.setState({
            ...this.state,timer: {
                ...this.timer,screenshotEnd: this.state.secondsElapsed,timingArray: newTimingArray,averageTimePerUnit: newTimingArray.reduce((a,0) / newTimingArray.length,},});
    }
}

如果始终使用captureTimetrue调用false,则可以使用以下方法使事情看起来更简洁:

captureTime = (startOrStop) => {
    if (startOrStop) {
        this.setState({ ...this.state,screenshotStart: this.state.secondsElapsed } });
        return;
    }
    const newTimingValue = this.state.secondsElapsed - this.state.timer.screenshotStart;
    const newTimingArray = [...this.state.timer.timingArray,newTimingValue];
    // etc

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-