开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
前言
最近在学习开发前端项目时,遇到了改变了state状态值,但UI页面却没有重新进行渲染,经过一整谷歌之后,发现React对于state的监听只会监听数据的第一层,因此我们需要使用浅拷贝或者深拷贝的方式去改变state第一层的状态,那么下面就来先学习一下浅拷贝和深拷贝的相关知识。
浅拷贝和深拷贝
几乎每种语言都有分浅深拷贝,即浅拷贝只会复制对象的第一层的值,深拷贝则会复制对象的所有属性,只有深拷贝才算是真正的复制对象。
- 浅拷贝:对于对象的基本数据类型属性直接进行复制、引用类型属性则复制对应地址
- 深拷贝:对于对象的基本数据类型属性直接进行复制、引用类型属性则创建一个新的对象来引用
下面来看下浅拷贝的例子,我们使用=赋值运算符就等于进行了一次浅拷贝,obj和newobj都引用同一片内存空间
let obj = {value:1}
let newObj = obj
obj.value = 100
console.log(obj,newObj);// {value: 100} {value: 100}
除了赋值运算符,通常浅拷贝还包括以下操作
// 使用ES6中的Object.assign({}, obj);
let obj = {value:1}
let newObj = Object.assign({},obj); // 分配一个新的内存地址,引用该obj对象
obj.value = 100
console.log(obj,newObj);// {value: 100} {value: 1}
// 使用ES7中的{ ...obj }
let obj = {value:1}
let newObj = Object.assign({},obj); // 分配一个新的内存地址,引用该obj对象
obj.value = 100
console.log(obj,newObj);// {value: 100} {value: 1}
// 数组的浅拷贝方式
// 使用concat方式或者使用slice方式
let arr = [1,2,3,4]
let newArr = arr.concat() // arr.slice(0)
arr[0] = 100
console.log(arr,newArr) // (4) [100, 2, 3, 4] (4) [1, 2, 3, 4]
关于深拷贝无非就是遍历属性进行上述"浅拷贝"操作,不过我们可以使用ES7的新特性来减少部分代码
let obj = {value:1,subObj:{subValue:2}}
let newObj = {...obj,subObj: {...obj.subObj}}
obj.subObj.subValue = 200
console.log(obj.subObj,newObj.subObj); // {subValue: 200} {subValue: 2}
异步问题
在了解TS中的浅拷贝和深拷贝后,对于React中出现修改state值,UI页面却没有改变的问题就有了大概的认识。当我们使用的对象具有多层属性时,就需要注意引用的问题。
但有时并不全是拷贝对象的问题,下面来看个例子
import React, {useState} from 'react';
import ReactDOM from 'react-dom/client';
function Demo() {
const [flag, setFlag] = useState( false );
return (
<div
onClick={() => {
setFlag( true );
console.log( 'flag', flag ); // 第一次点击时输出false
}}
>
DEMO
</div>
);
}
let root = ReactDOM.createRoot(
document.getElementById( "root" )
);
root.render(<Demo/>)
当我们第一次点击时,flag会输出false,则是因为useState是一个异步操作,因此当我们想读取到修改后的最新值的结果是不确定的。
不过React官方还提供了一种useState的重载方法,该方法的入参永远会指向最新的state值。
setFlag(true)
setFlag(preFlag=>{
console.log( 'flag', preFlag ); // 输出true
return preFlag
});