這是我參與 8 月更文挑戰的第 12 天,活動詳情查看:8月更文挑戰react
在介紹 Reselect
的以前,咱們首先要知道 Selector
這個概念,那麼什麼是 Selector
呢?我看到一種很形象的說法:想象一下你去便利店買可樂,你給店員說要可口可可,這時候店員就去給你拿一罐可口可樂,這時候,店員其實就是充當了 Selector
的角色,店員知道如何從各類商品裏拿到你要的可口可樂,具體來講 Selctor
有如下特色:git
Selector
知道從哪裏,以及如何去獲取數據的子集Selector
會返回數據的子集 用代碼來表示就是Reselect
做爲一個配合 Redux
(或其餘)的 使用的一款輕量型的狀態選擇庫:github
React
有一個原則:可以計算獲得的狀態必定要計算得到,state
中存儲最原始的數據。這樣的原則可能會帶來沒必要要的 render
並伴隨大量複雜的重複計算(Selector
),從而產生了性能的損耗,而 Reselect
給 Selector
提供了緩存的能力,避免了重複計算,從而提高性能。npm
# npm
npm install reselect
# yarn
yarn add reselect
複製代碼
簡易的價格計算器,須要緩存的計算屬性有:商品總價(subtotalSelector
)、稅費(taxSelector
)和總價(totalSelector
)。商品數量(shopItemsSelector
)和納稅(taxPercentSelector
)比例不變,就不會發生從新計算。數組
createSelector
傳入的 inputSelector
返回值會做爲最後一個函數參數的傳參,以 subtotalSelector
爲例子,shopItemsSelector
的返回值會做爲 items
傳入。緩存
import { createSelector } from 'reselect';
const shopItemsSelector = (state) => state.shop.items;
const taxPercentSelector = (state) => state.shop.taxPercent;
const subtotalSelector = createSelector(shopItemsSelector, (items) =>
items.reduce((subtotal, item) => subtotal + item.value, 0),
);
const taxSelector = createSelector(
subtotalSelector,
taxPercentSelector,
(subtotal, taxPercent) => subtotal * (taxPercent / 100),
);
const totalSelector = createSelector(
subtotalSelector,
taxSelector,
(subtotal, tax) => ({ total: subtotal + tax }),
);
const exampleState = {
shop: {
taxPercent: 8,
items: [
{ name: 'apple', value: 1.2 },
{ name: 'orange', value: 0.95 },
],
},
};
console.log(subtotalSelector(exampleState)); // 2.15
console.log(taxSelector(exampleState)); // 0.172
console.log(totalSelector(exampleState)); // { total: 2.322 }
複製代碼
拉出咱們以前的 列表項目 進行一下改造。當前頁面上的數據是經過 state.items
和 state.byId
計算獲得的一組對象數組,頁面上的任何狀態(state.search
)發生變化,都會引發列表數據的重計算(get data source
),看動圖 ⬇️性能優化
那麼,就這點來優化一下。markdown
安裝 reselect
,引入 { createSelector }
。網絡
import { createSelector } from 'reselect';
複製代碼
建立 dataSourceSelector
函數,監聽 items
和 byId
。antd
const getItems = (state) => state.items;
const getById = (state) => state.byId;
const dataSourceSelector = createSelector(getItems, getById, (items, byId) => {
console.log('reselect: get data sourc!');
if (!items) return [];
return items.map((id) => byId[id]);
});
複製代碼
在組件和 Redux 鏈接時,傳入新屬性 dataSource
,並取代原來的 getDataSource
方法。
const mapStateToProps = function (state) {
return {
...state.table,
dataSource: dataSourceSelector(state.table),
};
};
複製代碼
完成 ✅,除了 items
和 byId
外的其餘狀態的變化,再也不從新計算列表數據。
完整代碼,在這裏