当前位置: 首页 > news >正文

immutable与可变数据 不可变数据

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

 

  • immutable-js
  •  
  • redux
  •  
  • react.js
  •  
  • javascript

 8.5k 次阅读  ·  读完需要 20 分钟 

深复制与浅复制

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

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

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

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

es6-Object.assign()方法

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

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

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



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

obj1.a.b = 2;
//除非再次使用Object.assign嵌套方式赋值
//变化
console.log(obj) // { a:{b:2}, arr: [1,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,
    arr: [1, 2]
};
let obj1 = { ...obj }; //于obj1=obj一样
// 保持统一,尽量不要使用这样的替换(有可能造成不必要的麻烦)
obj1.a = 2
//...尽量使用这样的赋值形式
obj1 = { ...obj1 , a:2 }
//深复制
console.log(obj) // { a:20, arr: [1,2] };
console.log(obj1) // { a:2, arr: [1,2] };

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

//你可以将其转化为
let obj = {
    a: {
        b: 20
    },
    arr: [1, 2]
};
let obj1 = obj
obj1 = Object.assign({}, obj1, {
    a: Object.assign({}, obj1.a,{b:2})
});
console.log(obj) //{ a:{b:20}, arr: [1,2] }
console.log(obj) //{ a:{b:2}, arr: [1,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;

  • Immutable.js及在React中的应用
  • Immutable 详解及 React 中实践
  • Immutable as React state

(封装好的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, nextProps, 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无效

相关文章:

  • 辞职:也要像个君子般离开
  • redux connect 原理解析 传入的mapstate 和mapdispatch connect()第一次执行后返回一个函数 这个函数接收到的参数是一个组件 对组件进行加工返回
  • Flash Lite 与 J2ME 分析比较
  • react dva 复习subscriptions: setup
  • pm2 pm2 --help 使用指南
  • 驳斥一些不看好Flash的观点
  • eruda . 移动端console输出日志调试工具
  • referer
  • 2006年3GSM全球大会10佳手机
  • H5 postMessage方法 以及监听
  • 浏览器和node某方面执行顺序不一样
  • 大餐还是鸡肋?让我们谈谈Flash Lite
  • gulp中的同步异步问题
  • 小试一把 64 位 Windows 编程
  • 什么是options请求?为什么会有options请求?
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 345-反转字符串中的元音字母
  • android图片蒙层
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CSS 专业技巧
  • JavaWeb(学习笔记二)
  • Java教程_软件开发基础
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Redis中的lru算法实现
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 给github项目添加CI badge
  • 警报:线上事故之CountDownLatch的威力
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 2017年360最后一道编程题
  • 大数据全解:定义、价值及挑战
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • (16)Reactor的测试——响应式Spring的道法术器
  • (k8s中)docker netty OOM问题记录
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (二)windows配置JDK环境
  • (二)构建dubbo分布式平台-平台功能导图
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (南京观海微电子)——I3C协议介绍
  • ***详解账号泄露:全球约1亿用户已泄露
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET DataGridView数据绑定说明
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • []C/C++读取串口接收到的数据程序
  • [C++] 如何使用Visual Studio 2022 + QT6创建桌面应用
  • [cocos creator]EditBox,editing-return事件,清空输入框
  • [flume$2]记录一个写自定义Flume拦截器遇到的错误
  • [GDOUCTF 2023]<ez_ze> SSTI 过滤数字 大括号{等
  • [HDOJ4911]Inversion
  • [hdu 1247]Hat’s Words [Trie 图]