React引用数据类型与immutable.js的使用实例
代码最新内容请在github阅读,github上托管了完整的使用immutable.js管理state的代码实例,也欢迎您star和issue 一,React中浅层次拷贝的问题例子1我们给出下面的事实: const detail = {name:'qinlaing',school:{loc:'dalian'}}
const copy = Object.assign({},detail);
copy.school.loc ="北京";
//此时你会发现我们的detail.school.loc也变成了"北京"了
copy.school === detail.school
//此时copy.school和detail.school指向同一个对象,引用相同,一个值被修改那么另一个同样被修改
这也就是告诉我们,如果你要对整个state对象进行处理,那么你一定要深度拷贝,而不是浅层次的拷贝,因为Object.assign这种类型会导致copy后的结果发送变化,而 onChange = (relation)=>{
this.setState({ownRelationship:relation});
this.props.onSave&&this.props.onSave(Transform2RawRelationship(deepCopy(this.state.ownRelationship)));
}
我先给出场景:假如我们每次删除一行数据的时候都会调用onChange函数,而onChange回调函数会接收到最新的 this.props.onSave&&this.props.onSave(Transform2RawRelationship(deepCopy(this.state.ownRelationship)));
你的关注点应该是在 解答:如果我们不做深度拷贝,那么在方法 那么做一次浅层次的复制是否可以? 解答:不可以。就像文章第一个例子展示的情况,我们虽然使用Object.assign做了一次浅层次的复制,但是当我们在方法 例子2import '_' from 'lodash';
const Component = React.createClass({
getInitialState() {
return {
data: { times: 0 }
}
},handleAdd() {
let data = this.state.data;
//data和this.state.data指向了同一个数据,即data:{times:0}这个引用类型
data.times = data.times + 1;
//此时你会发现我们的this.state.data和data.times都会发生变化了,因为他们指向同一个引用
this.setState({ data: data });
//我们此时的data还是原来的data,只是其times属性值发生变化了,所以SCU判断的时候要小心
console.log(this.state.data.times);
//此时this.state.data.times是已经修改后的数据了
}
}
盗用别人的一个例子只是为了说明,如果是引用类型的时候不管是浅层次的拷贝,还是压根不拷贝都是很可能产生副作用的。所以上面的代码我们经常会做一次深拷贝,一方面是为了保持state的不可变特性,还有另一方面就是为了使得SCU(shouldComponentUpdate)能够正常判断,而不是两次nextProps或者nextState指向同一个引用。这也是为什么react官方建议将state设置为不可变的原因。 import '_' from 'lodash';
const Component = React.createClass({
getInitialState() {
return {
data: { times: 0 }
}
},handleAdd() {
let data = _.cloneDeep(this.state.data);
data.times = data.times + 1;
this.setState({ data: data });
// 如果上面不做 cloneDeep,下面打印的结果会是已经加 1 后的值。
console.log(this.state.data.times);
}
}
二,React中引用类型导致组件不更新首先看下面的例子: class Parent extends React.Component{
state = {
school:{
location:"Dalian",name :"DLUT"
}
}
onClick =()=>{
this.setState({school:{location:"HUNan",name:"湖南大学"}});
// const newSchool = this.state.school;
// newSchool.location = "HUNan";
// this.setState({school:newSchool});
}
render(){
return (
<div> <Child school={this.state.school}/> <button onClick={this.onClick}>点击我改变state</button> </div> ) } } class Child extends React.Component{ shouldComponentUpdate(nextProps,nextState,nextContext){ return true; } render(){ return ( <div> 学校名称:{this.props.school.name} 学校位置:{this.props.school.location} </div> ) } } ReactDOM.render( <Parent/>,document.getElementById('example') );
我们通过this.state.school将上层组件的一个引用传入到子组件中,于是子组件就可以拿到这个上层组件的引用了。当我们点击按钮修改state的时候,即调用下面的逻辑: this.setState({school:{location:"HUNan",name:"湖南大学"}});
如果你在Child组件的SCU方法中做如下判断,那么判断的结果就是false: console.log('this.props.school===nextProps.school',this.props.school===nextProps.school);
原因在于,我们其实是采用了一个新的对象来 但是如果你将上面setState用下面几句代码替代,问题就会出现了 const newSchool = this.state.school;
newSchool.location = "HUNan";
this.setState({school:newSchool});
//其实newSchool和this.state.school指向的是同一个引用(即指针),因此导致下层组件接受到的this.props.school依然是同一个引用。即,此时我们的变量newSchool保存的是引用而不是一个对象,不过其引用的值指向的是该对象
此时我们的下面的判断就会得到true: console.log('this.props.school===nextProps.school',this.props.school===nextProps.school);
原因很简单:在父组件中通过newSchool保存了一个 this.setState({school:{location:"HUNan",name:"湖南大学"}});
三,immutable.js的使用immutable.js例子1var map1 = Immutable.Map({a:1,b:2,c:3,school:{
location:"DaLian",name :"DLUT"
}});
var map2 = map1.set('b',50);
//(1)我这里仅仅设置了b的值,那么其他的值都会共享
console.log('引用相等',map1===map2);
//(2)immutable.js中每次返回的引用都是不一样的,此处返回false
console.log('school的引用没有变化',map2.school===map1.school);
//(3)immutable.js中没有变化的对象将会共享,所以此处返回true
var map3 = {a:1,c:3}
var map4 = map3;
//(4)map4拿到的是map3的指针,所以一个变化后另外一个也会变化,但是变化的是值,引用本身是不变化的,所以map3===map4返回true
map4.c =4;
console.log('map3===map4',map3===map4);
immutable.js例子2var map1 = Immutable.fromJS(
{a:1,home:{
location:{
name:'Hunan huaihua',street:405
}
},school:{
location:"DaLian",name :"DLUT",ratio:{
Hunan:698,ZheJiang : 900
}
}
});
var map2 = map1.updateIn(['school','ratio',"ZheJiang"],value => value + 1);
console.log('引用相等',map1===map2);
//打印false
const updatedRatio = map1.getIn(["school","ratio"])===map2.getIn(["school","ratio"]);
console.log('map2在map1的基础上更新了ratio此时ratio引用不再相等',updatedRatio);
//打印false
const updatedSchool = map1.getIn(["school"]) === map2.getIn(['school']);
console.log('map2在map1的基础上更新了ratio此时school引用不再相等',updatedSchool);
//打印false
const updatedObject = map1 === map2;
console.log('map2在map1的基础上更新了ratio此时对象引用不再相等',updatedObject);
//打印false
const updatedHome = map1.getIn(["home"]) === map2.getIn(['home']);
console.log('map2在map1的基础上更新了ratio此时home引用依然相等',updatedHome);
//打印true
const updatedHomeLocation = map1.getIn(["home","location"]) === map2.getIn(['home','location']);
console.log('map2在map1的基础上更新了ratio此时home的location引用依然相等',updatedHomeLocation);
//打印true
我想要通过这两个例子给自己一个直观的认知,如果你通过immutable.js的方法修改了对象的某一个属性的时候,该属性的所有的父级属性的引用都会发生改变,而其他属性的引用都是共享的。这部分网上都有的说,但是通过代码展现出来也能够加深理解。因此在immutable.js中你常常会看到这样的SCU: import { is } from 'immutable';
shouldComponentUpdate: (nextProps = {},nextState = {}) => {
const thisProps = this.props || {},thisState = this.state || {};
if (Object.keys(thisProps).length !== Object.keys(nextProps).length ||
Object.keys(thisState).length !== Object.keys(nextState).length) {
return true;
}
for (const key in nextProps) {
if (!is(thisProps[key],nextProps[key])) {
return true;
}
}
for (const key in nextState) {
if (thisState[key] !== nextState[key] || !is(thisState[key],nextState[key])) {
return true;
}
}
return false;
}
其主要通过immutable.js的is方法来判断数据是否发生变化,Immutable.js提供了简洁高效的判断数据是否变化的方法,只需 === 和 is比较就能知道 而且我们的 继续阅读请前往我的github 参考文献: 如何有效地提高react渲染效率–深复制,浅复制,immutable原理 Immutable 详解及 React 中实践 react组件性能优化探索实践 正式学习 React(三)番外篇 reactjs性能优化之shouldComponentUpdate (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |