React中的state

先提个问题: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所创建的新对象中所包含的嵌套对象和数组,仍然指向的是原对象中的嵌套对象和数组。

 

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来替代$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著  杜伟、柴晓伟、涂曙光译

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值