React 数据为什么要使用immutable方式?浅复制与深复制思考

深复制与浅复制

let obj = {
    a: 1,arr: [1,2]
};
let obj1 = obj;            //浅复制
obj1.a = 2

console.log(obj) // { a:2,2] };

//同样的方式
let obj = {
    a: 1,2]
};
let obj2 = deepCopy(obj);  //深复制
obj2.a = 2
console.log(obj) // { a:1,2] };

因为JavaScript存储对象都是存地址的,所以浅复制会导致 obj 和 obj1
指向同一块内存地址,大概的示意图如下。而深复制一般都是开辟一块新的内存地址,将原对象的各个属性逐个复制出去。

es6-Object.assign()方法

深复制只有一层,之后为浅复制(除非再次使用Object.assign嵌套方式赋值)

let obj = {
    a: 1,2]
};
let obj1 = Object.assign({},obj);

obj1.a = 2
//不变
console.log(obj) // { a:1,2] };



let obj = {
    a: {
        b: 20
    },obj);

obj1.a.b = 2;
//除非再次使用Object.assign嵌套方式赋值
//变化
console.log(obj) // { a:{b:2},2] };

为什么使用不可变(immutable)的数据?

(pureRender结合immutable,见末尾)

下面是项目中实际的一个例子

第一种方式

//recduer.js(cart)第一种方式
case types.CART_PUT_MAIN + '_SUCCESS':
    //更新数据
    carts = state.main.carts; // carts 选中的id数组
    id = action.param.id;
    newState = {
        ...state,main:{
            ...state.main,itemObj:{
                ...state.main.itemObj,[id]:{
                    ...state.main.itemObj[id],quantity:action.param.quantity
                    
                }
            }
        }
    };
    sum = sumCommon(carts,newState.main.itemObj);
    newState = {
        ...newState,main:{
            ...newState.main,...sum
        }
    };
    return newState;

让我们来看一下对数据层的变化:

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顾名思义是接收到的next->props,输出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是当前的props的值,因为使用的是类immutable的方式,所以数据不变;
}

第二种方式

//recduer.js(cart)第一种方式
case types.CART_PUT_MAIN + '_SUCCESS':
    newState = Object.assign({},state);
    carts = newState.main.carts; // carts 选中的id数组
    id = action.param.id;
    //浅复制
    newState.main.itemObj[id].quantity = action.param.quantity;;
    sum = sumCommon(carts,newState.main.itemObj);

    newState = Object.assign({},newState,{
        main: Object.assign({},newState.main,sum)
    });
    return newState;

让我们来再来看一下对数据层的变化:

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顾名思义是接收到的next->props,输出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是当前的props的值,而这个由于浅复制,这个值被改变了
}

为了让数据变化更加可测,我们应当使用深复制相关,让我们自己的数据更加安全

处理方法一:es7 ... 的方式

直接{...obj}赋值属于浅复制,在修改值时{...obj,a:1}就起到了类深复制的效果
更新一个 Object ,则:

let obj = {
    a: 0,b: 20,}
obj = {...obj,a: obj.a + 1}

而不是:

obj.a = obj.a + 1

同样的为了避免对 Object 的 in-place editing,数组也是一样:

let arr = [
    { id: 1,a: 1}
]
arr = [...arr,{ id: 2,a: 2} ]

而不是:

let arr = [
    { id: 1,a:1}
]
arr.push({ id: 2,a,2});

以这样的方式,无需 Immutable.js ,我们可以让应用程序状态是 不可变(Immutable) 的。

...注意事项及要求

let obj = {
    a: 20,2]
};
let obj1 = { ...obj }; //于obj1=obj一样

obj1.a = 2 //不能使用这样赋值
//变化 浅复制
console.log(obj) // { a:2,2] };
//...必须改用这样的赋值形式
obj1 = { ...obj1,a:2 }
//深复制
console.log(obj) // { a:20,2] };
console.log(obj1) // { a:2,2] };

...与Object.assign属于一个道理(这里和层级相关)

//你可以将其转化为
let obj = {
    a: {
        b: 20
    },2]
};
let obj1 = obj
obj1 = Object.assign({},obj1,{
    a: Object.assign({},obj1.a,{b:2})
});
console.log(obj) //{ a:{b:20},2] }
console.log(obj) //{ a:{b:2},2] }

所以尽量使用...代替Object.assign

处理方法二:使用immutable.js

为什么需要使用immutable.js

之前方式的多层嵌套

//深复制(类immutable)
newState = {
    ...state,main:{
        ...state.main,itemObj:{
            ...state.main.itemObj,[id]:{
                ...state.main.itemObj[id],prop:action.param.props_str,product_id:action.param.product_id,price:action.param.price
            }
        }
    }
};
//浅复制
newState.main.itemObj[id].prop = action.param.props_str;
//immutable.js方式
...参考immutable的api吧,暂时就不提供了--!

PureRenderMixin使用请参考以下内容

简单的说就是数据变化,比较前后两次的数据是否相同,判断是否重新render;否则你的父容器一改变数据,所有的子组件都重新渲染了,为了增加性能请使用pureRender;

(封装好的PureRender如下:)

'use strict';

import { is } from 'immutable';

let hasOwnProperty = Object.prototype.hasOwnProperty;
function shallowEqual(objA,objB) {
    if (objA === objB || is(objA,objB)) {
        return true;
    }

    if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
        return false;
    }

    let keysA = Object.keys(objA);
    let keysB = Object.keys(objB);

    if (keysA.length !== keysB.length) {
        return false;
    }
    let bHasOwnProperty = hasOwnProperty.bind(objB);
    for (let i = 0; i < keysA.length; i++) {
        if (!bHasOwnProperty(keysA[i]) || !is(objA[keysA[i]],objB[keysA[i]])) {
            return false;
        }
    }

    return true;
}
function shallowCompare(instance,nextProps,nextState) {
    return !shallowEqual(instance.props,nextProps) || !shallowEqual(instance.state,nextState);
}
function shouldComponentUpdate(nextProps,nextState) {
    return shallowCompare(this,nextState);
}
function pureRenderDecorator(component) {
    component.prototype.shouldComponentUpdate = shouldComponentUpdate;
}
module.exports = pureRenderDecorator;

/*使用方式*/
import pureRender from 'pure-render-decorator';
//babel配置中引入一个transform-decorators-legacy插件
@pureRender
class XXX extends React.Component {
    //...
}

PureRender的使用要求:对于子组件需要什么参数传递什么,不要把一大块无用的数据引入,否则两次传入的this.props可能始终会不一样,导致PureRender无效

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

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom