1. 程式人生 > 實用技巧 >React Redux 元件更新/渲染 原理 connect之mapStateToProps 看這篇就夠了!比中文文件好用!

React Redux 元件更新/渲染 原理 connect之mapStateToProps 看這篇就夠了!比中文文件好用!

本文深入淺出mapStateToProps,解答:

  • 為什麼修改state,元件未渲染/更新?
  • 如何從新舊state判斷更新的值、未更新的值,從而決定是否re-render?

Redux中,state作為單一的資料來源,眾所周知,每次更新state都要通過return { ...state, others }來返回一個新的state,但是它是怎麼來判斷一些元件到底要不要re-render(重新整理、重渲染)呢?尤其是當state層次很深的時候,會有效率問題、該重新整理時不重新整理的問題嗎?

其實關鍵在於這個connect()函式的第一個引數:mapStateToProps。下面舉個例子:

/*
假設state的結構如下: state = { user: {username: 'root', name: '根'}; globals: {showMenu: true, showModal: false}; }; */ function mapStateToProps(state) { return { user: state.user, // 比如常見的獲取使用者資訊 showMenu: state.globals.showMenu, // 比如state層次有多層,可以直接獲取到那個深層的資料 }; } export default connect(mapStateToProps)(MyComponent);

眾所周知,mapStateToProps必須是個純函式,只要有相同的輸入,一定會有相同的輸出,不會有任何副作用,沒有非同步操作。輸入是state,輸出是一個物件,這個物件會變為被connect元件的props

其實,這個函式通常是選取了state的一個子集,通過props對映到了元件上。如果這個子集更新了,那麼元件就會re-render。具體原理、過程如下:

原理

state更新時(即nextState !== state,注意這裡用===來比較,所以每次更新state需要用文章開頭的方式來更新),
react-redux會呼叫所有的mapStateToProps函式(所以每個mapStateToProps

函式應該很快能運算結束,不然會成為應用效能瓶頸),
針對每次呼叫mapStateToProps,檢查函式的結果(是個物件)的每個key的value跟上一次對應的value是否一致(這裡也是用===來比較!)如果所有value都一致,不會渲染這個元件;如果有一個value變了,就重新渲染。

PS. 所以react-redux中對mapStateToProps的結果的比較是淺比較,即會遍歷所有key來判斷是否相等,對每個key的value的判斷方法是===。所以,要搞清楚“===”、“淺比較”、“深比較”的差別。

舉例說明

用上面舉例提到的mapStateToProps函式。(所以每次會比較返回值key是user的value和key是showMenu的value是否相等)

function mapStateToProps(state) {
  return {
    user: state.user,
    showMenu: state.globals.showMenu,
  };
}

假設有初始state如下:

state = {
  user: {username: 'root', name: '根'};
  globals: {showMenu: true, showModal: false};
};

案例1:一個正確示範

比如有一個某個操作dispatch了一個action,返回新的state的程式碼如下:

return { ...state, user: {username: 'foo', name: 'bar'} };

通過對比,發現showMenu還是原來的showMenu,但是user變成了新的物件,所以,重新渲染!

案例2:一個正確示範,可看出redux對效能的優化,reducer要用淺拷貝!

比如有一個某個操作dispatch了一個action,返回新的state的程式碼如下:

const globals = {...state.globals, showModal: true};
return { ...state, globals };

通過對比,發現showMenu還是原來的showMenu(因為都是true,發現相等),而且user也是原來的user,所以,不會重新渲染!
注意,這裡的state.globals已經不是原來的state.globals了,但是函式返回的物件中:key是showMenu的value沒變,所以不會重新渲染。

思考1:如果元件中用到的是某個state的某個部分的值,mapStateToProps函式一定要儘可能細化到它,這有助於優化!
思考2:寫reducer更新state時,淺拷貝就夠了,千萬不要深拷貝!

// 如果有這樣的狀態
state = {
  articles: {size: 'big', list: ['1', '2', '3', '4']},
};
// 想更新state.articles.size這樣寫,好!
const articles = { ...state.articles, size: 'small'};
return { ...state, articles };
// 這樣寫會導致與list有關的元件而與size無關的元件也被重新渲染,不好!而且深拷貝效能損耗更多!
const articles = {size: 'small', list: [...state.articles.list]};
return { ...state, articles };

案例3,錯誤示範

比如有一個某個操作dispatch了一個action,返回新的state的程式碼如下:

const user = state.user;
user.username = 'foo';
user.name = 'bar';
return { ...state, user };

通過對比,發現showMenu還是原來的showMenu,而user也是原來的user(強調:state.user依然是原來的,並沒有建立或複製一個新的物件,const user = state.user;只是複製了引用,不算淺拷貝;不過還是要注意返回的state跟之前的state已經不是同一個了),所以,不會重新渲染!

怎麼修改?答案如下:

const user = {
  ...state.user,
  username: 'foo',
  name: 'bar',
};
return { ...state, user };

案例4,更錯誤的示範

比如有一個某個操作dispatch了一個action,返回新的state的程式碼如下:

state.user = {username: '', name: ''};
return state;

這個時候mapStateToProps函式都懶得理你,它不會執行的。因為state根本沒變。

原文連結:https://blog.csdn.net/kd_2015/article/details/105277509