添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
不拘小节的口罩  ·  ORACLE ...·  1 年前    · 
潇洒的课本  ·  java ...·  1 年前    · 
读研的枕头  ·  跟着Nature ...·  1 年前    · 
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

引言

先提个问题:React中 this.setState({xxx:''}) this.state.xxx='' 有区别吗?

答案:有区别的。

this.state通常是用来 初始化state 的,this.setstate是用来 修改state 值的。

如果你初始化了state之后再使用this.state,之前的state会被覆盖掉,如果使用this.setState,只会替换掉相应的state值。

一、this.setState({})

  1. this.setState({})会触发render方法,重新渲染界面。而this.state.xxx=’’ 不会重新加载UI。
  2. this.setState({})是异步的。

二、this.state.xxx=''

在什么场景会使用this.state.xxx呢?

我在写了两个button,点击button更新改变state的值,然后将state作为参数请求数据,自动刷新列表UI的时候,出现了问题。

例子:

constructor(){
    this.state={
    couponStatus : 0 ,
render() {
        <SegmentedBar
            justifyItem='fixed'
            indicatorType='customWidth'
            indicatorLineColor='#f5e220'
            indicatorWidth={90}
            animated={false}
            onChange={(index) => {
                this.setState(
                        couponStatus:index;
复制代码

上面这段代码是无法达到预期效果的,为什么呢?因为setState,是异步的,且有一定延迟,这就导致了有时候后面列表加载数据已经完成了,state却还没有改变,导致的直接结果就是数据错乱。于是,我们把代码略微修改一下。

this.state.couponStatus = index;
//别忘了手动刷新列表UI
this.refs.couponListView.refresh();
复制代码

这样就完美的解决了。

三、不变性

从上述例子中可以看出,和this.props类似,可以把this.state当作只读属性。应当总是使用setState方法来更新组件UI的状态,而不应直接修改this.state。当this.setState()被调用时,React会更新界面。这是最常见的情况,但也有例外,比如可以使通过生命周期函数shouldComponentUpdate()返回false,从而避免界面更新。

而标题所讲的不变性,指不去修改对象,而是直接替换这个对象。

假如现在有一个有状态组件,来显示一家航空公司的机票:

import React,{Component} from 'react';
class Test extends Component{
    constructor(){
        super(...arguments)
        this.state={
            passengers:[
                'Simmon',
                'Taylor'
    render(){...}
复制代码

现在需要将一个新乘客添加到passengers数组中。如果不小心,就会无意中直接修改组件的state对象。例如:

let newPassengers = this.state.passengers;
//在js中,对象和数组都是以引用方式传递的,意味着此行代码并未创建数组的一个副本,
//只是创建了一个新引用,而引用指向的是当前组件的state中的那个数组。
//因此在下一个代码中使用push方法是直接在修改state。
newPassengers.push('Jay');
this.setState({
    passengers: newPassengers
复制代码

要在js中创建一个数组的实际副本,需要使用非侵入式数组方法,也就是说,使用那些会返回一个新数组,而非直接对原数组进行修改的方法。map、filter、concat、slice都是这种形式的非侵入式数组方法。

在js中,基于原来的对象生成新的对象还有其他一些方式,例如使用Object.assign。Object.assign具有兼容性问题,不过该问题可用Babel解决,此处不详述。

下面利用Array的concat方法来实现添加的功能:

let newPassengers = this.state.passengers.concat('Jay');
this.setState({
    passengers: newPassengers
复制代码

四、嵌套对象(浅拷贝和深拷贝)

1、深拷贝与浅拷贝的概念

如何区分深拷贝与浅拷贝?

简单点来说,就是假设B复制了A,当修改B时,看A是否会发生变化,如果A也跟着变了,说明这是浅拷贝;如果A没变,那就是深拷贝。

1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

为什么要使用深拷贝?

我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

深拷贝的要求程度?

我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度: 是仅“深”拷贝第一层级的对象属性或数组元素 还是递归拷贝所有层级的对象属性和数组元素?

2、嵌套对象

在大多数情况下,数组的非侵入式方式和Object.assign已经可以完成我们想要的操作,但是如果state中包含嵌套对象或数组,那么情况就变得棘手。因为Js的特性:对象和数组都通过引用方式传递,并且数组的非侵入式方式和Object.assign都不会做深拷贝。在实践中,这意味着在你通过数组非侵入式方法和Object.assign所创建的新对象中所包含的嵌套对象和数组,仍然指向的是原对象中的嵌套对象和数组。

image

3、React不变性助手——update

update函数应用到普通的JS对象和数组之上,可将它们包装成不可变的对象:函数不会真正修改这些对象,而总是返回一个新的可变的对象。

官网: react-addons-update - npm

安装:npm i react-addons-update

导入:import update from 'react-addons-update'

说明:update函数接收两个参数,第一个参数是想要更新的对象和数组。第二个参数是一个对象,它描述了你想要在何处进行何种修改。

举例:

let student = {name:'Jonn',grades:['A','B','C']}
//要创建这个对象的一个修改grades后的副本,update函数的用法如下:
let newStudent = update(student,{grades:{$push:['A']}})
复制代码

对象{grades:{$push:['A']}}从左到右表明了update函数应当:

  • 定位到grades属性(修改应该应用在“何处”)
  • 将一个新值添加到数组中(应该应用到“何处”修改“)

如果想要完全修改整个数组,可使用命令set来替代set来替代set来替代push:

let newStudent = update(student,{grades:{$set:['A','A','B']}})
复制代码

对于对象中可以有多少层嵌套,并没有任何限制。适当时候可使用数组索引:

a={
    code:[
        {company:'GL',No:'9'},
        {company:'TM',No:'3'}
b=update(a,{
    code:{
        0:{$set:{company:'AZ',No:'5'}}
复制代码

4、update的命令

命令 描述
$push 类似与Array的push函数,它向一个数组的尾部添加一个或多个元素。
$unshift 类似于Array的unshift函数,它在一个数组的头部添加一个或多个元素。
$splice 类似于Array的splice函数,它通过从数组中移除元素且/或向数组中添加元素,来修改一个数组的内容。和Array.splice主要的语法区别是你需要提供一个元素为数组的数组来作为参数,作为参数的数组中的每一个数组包含了要对数组进行splice操作的参数。
$set 完整地替换掉整个目标。
$merge 将给定对象的键合并到目标对象中。
$apply 将当前的值传给一个函数,在函数中对传入的值进行修改,然后使用函数的返回值作为结果。

代码举例:

let a = [1,2,3]
let b = update(a,{$push:[4]})
// => [1,2,3,4]
let a = [1,2,3]
let b = update(a,{$unshift:[0]})
// => [0,1,2,3]
let a = [1,2,'a']
let b = update(a,{$splice:[[2,1,3,4]]})
// => [1,2,3,4]
let ob = {a:5,b:3}
let newOb = update(ob,{$merge:{b:6,c:7}})
// => {a:5,b:6,c:7}
let ob = {a:5,b:3}
let newOb = update(ob,{b:{$apply:(value)=>value*2}});
// => {a:5,b:6}
复制代码

参考链接:

1. react native中this.setState({xxx:''})与this.state.xxx='' 有区别吗?

2. 一篇文章彻底搞懂浅拷贝和深拷贝

3.书籍《React开发实战》——Cssio de Sousa Antonio著  杜伟、柴晓伟、涂曙光译