react就像facebook自己说的,真的就是个写视图的库而已。当初学者开始着手项目时,会发现想将项目功能实现或扩展、优化,总需要一些其他的库或工具函数。今天想来写一个比较常用且实用的库,reselect。
当我们接触redux的时候,我们发现,虽然用redux之后要额外写一堆东西,但确实让数据流通变得规范、状态管理变得可控。通过用react-redux提供的connect函数,mapStateToProps成为视图组件获取state 的唯一途径。
既然mapState成为组件获取state的唯一途径,那么当我们想要从mapState提供的状态中计算一些需要的数据时,比如计算当前要展示的todolist的项目:
const mapState = (state)=>({
todos:state.todos,
filter:state.filter
});
显然,我们要么在mapState函数中计算要显示的todos:
const mapState = (state)=>({
todos:state.todos,
filter:state.filter,
visibleTodos:getVisibleTodos(state.todos,state.filter)
});
要么将getVisibleTodos这一步放到组件内部。
可是这里面其实隐藏了一个问题,就是每次store.state update后,redux都会调用一次mapState,不论todos和filter的值是否改变。当我们的app足够大、组件众多时,这里面大量冗余计算的问题就值得重视了。
reselect这个库的目的,就是帮我们把这部分的计算提取到一个叫selector的函数中:
//selector.js
export const todosSelector = (state) => state.todos;
export const filterSelector = (state) => state.filter;
export const visibleTodosSelector = createSelector(
[todosSelector,filterSelector],
(todos,filter)=>{return getVisibleTodos(todos,filter)} //这里假设已经定义一个getVisibleTodos函数用来返回要显示哪些todo项
);
//container.js
import {visibleTodosSelector}
const mapState = (state)=>({
todos:visibleTodosSelector(state)
});
reselect的好处是,当todoSelector和filterSelector没有发生变化时,visibleTodosSelector不会重新计算。也就是说,当state中的todos和filter没有变化时,reselect会直接返回上一次计算缓存的结果,避免多余的重复计算,此时mapState实际上从原本的:
(state)=>{{
stateA:state.stateA,
stateB:state.stateB
}}
变成:
(state)=>({
stateA:stateASelector(state),
stateB:stateBSelector(state)
})
在应用中实用reselect,可以帮我们大幅减少无必要的计算,从而提高性能。
reselect还有很多小特性,这里就不赘述了。