React 數(shù)據(jù)為什么要使用immutable方式担神?

深復(fù)制與淺復(fù)制

let obj = { a: 1, arr: [1, 2]};let obj1 = obj; //淺復(fù)制
obj1.a = 2console.log(obj) // { a:2, arr: [1,2] };
//同樣的方式
let obj = { a: 1, arr: [1, 2]};
let obj2 = deepCopy(obj); //深復(fù)制
obj2.a = 2console.log(obj) // { a:1, arr: [1,2] };

因為JavaScript存儲對象都是存地址的,所以淺復(fù)制會導(dǎo)致 obj 和 obj1指向同一塊內(nèi)存地址演痒,大概的示意圖如下亲轨。而深復(fù)制一般都是開辟一塊新的內(nèi)存地址,將原對象的各個屬性逐個復(fù)制出去鸟顺。

es6-Object.assign()方法

深復(fù)制只有一層惦蚊,之后為淺復(fù)制(除非再次使用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)的數(shù)據(jù)?

(pureRender結(jié)合immutable讯嫂,見末尾)
下面是項目中實際的一個例子
第一種方式

//recduer.js(cart)第一種方式
case types.CART_PUT_MAIN + '_SUCCESS':
    //更新數(shù)據(jù)
    carts = state.main.carts; // carts 選中的id數(shù)組
    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;

讓我們來看一下對數(shù)據(jù)層的變化:

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顧名思義是接收到的next->props蹦锋,輸出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是當前的props的值,因為使用的是類immutable的方式欧芽,所以數(shù)據(jù)不變莉掂;
}

第二種方式

//recduer.js(cart)第一種方式
case types.CART_PUT_MAIN + '_SUCCESS':
    newState = Object.assign({}, state);
    carts = newState.main.carts; // carts 選中的id數(shù)組
    id = action.param.id;
    //淺復(fù)制
    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;

讓我們來再來看一下對數(shù)據(jù)層的變化:

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顧名思義是接收到的next->props,輸出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是當前的props的值千扔,因為使用的是類immutable的方式憎妙,所以數(shù)據(jù)不變;
}

為了讓數(shù)據(jù)變化更加可測曲楚,我們應(yīng)當使用深復(fù)制相關(guān)厘唾,讓我們自己的數(shù)據(jù)更加安全

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

直接{...obj}賦值屬于淺復(fù)制,在修改值時{...obj,a:1}就起到了類深復(fù)制的效果更新一個 Object 洞渤,則:

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

而不是:

obj.a = obj.a + 1

同樣的為了避免對 Object 的 in-place editing阅嘶,數(shù)組也是一樣:

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 ,我們可以讓應(yīng)用程序狀態(tài)是 不可變(Immutable) 的炭懊。

...注意事項及要求

let obj = {
    a: 20,
    arr: [1, 2]
};
let obj1 = { ...obj }; //于obj1=obj一樣

obj1.a = 2 //不能使用這樣賦值
//變化 淺復(fù)制
console.log(obj) // { a:2, arr: [1,2] };
//...必須改用這樣的賦值形式
obj1 = { ...obj1 , a:2 }
//深復(fù)制
console.log(obj) // { a:20, arr: [1,2] };
console.log(obj1) // { a:2, arr: [1,2] };

...與Object.assign屬于一個道理(這里和層級相關(guān))

//你可以將其轉(zhuǎn)化為
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
之前方式的多層嵌套

//深復(fù)制(類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
            }
        }
    }
};
//淺復(fù)制
newState.main.itemObj[id].prop = action.param.props_str;
//immutable.js方式
...參考immutable的api殖告!

Immutable.js及在React中的應(yīng)用

Immutable 詳解及 React 中實踐

Immutable as React state

PureRenderMixin使用請參考以下內(nèi)容

簡單的說就是數(shù)據(jù)變化,比較前后兩次的數(shù)據(jù)是否相同坚踩,判斷是否重新render;否則你的父容器一改變數(shù)據(jù),所有的子組件都重新渲染了惋耙,為了增加性能請使用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, 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的使用要求:對于子組件需要什么參數(shù)傳遞什么熊昌,不要把一大塊無用的數(shù)據(jù)引入绽榛,否則兩次傳入的this.props可能始終會不一樣,導(dǎo)致PureRender無效

原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末婿屹,一起剝皮案震驚了整個濱河市灭美,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昂利,老刑警劉巖届腐,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铁坎,死亡現(xiàn)場離奇詭異,居然都是意外死亡犁苏,警方通過查閱死者的電腦和手機硬萍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來围详,“玉大人朴乖,你說我怎么就攤上這事≈蓿” “怎么了寒砖?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嫉拐。 經(jīng)常有香客問我哩都,道長,這世上最難降的妖魔是什么婉徘? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任漠嵌,我火速辦了婚禮,結(jié)果婚禮上盖呼,老公的妹妹穿的比我還像新娘儒鹿。我一直安慰自己,他們只是感情好几晤,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布约炎。 她就那樣靜靜地躺著,像睡著了一般蟹瘾。 火紅的嫁衣襯著肌膚如雪圾浅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天憾朴,我揣著相機與錄音狸捕,去河邊找鬼。 笑死众雷,一個胖子當著我的面吹牛灸拍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播砾省,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鸡岗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了编兄?” 一聲冷哼從身側(cè)響起轩性,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翻诉,沒想到半個月后炮姨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捌刮,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年舒岸,在試婚紗的時候發(fā)現(xiàn)自己被綠了绅作。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛾派,死狀恐怖俄认,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洪乍,我是刑警寧澤眯杏,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站壳澳,受9級特大地震影響岂贩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巷波,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一萎津、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抹镊,春花似錦锉屈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至终佛,卻和暖如春俊嗽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背查蓉。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工乌询, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榜贴,地道東北人豌研。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像唬党,于是被迫代替她去往敵國和親鹃共。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容