redux做爲大型應用的狀態管理工具,若是想配合react使用,須要藉助react-redux。 redux主要完成兩件事情:react
那麼,若是想要將react和redux搭配使用,就須要react組件能夠根據redux中所存儲的狀態(store)更新view。 而且能夠改變store。其實react-redux主要就是完成了這兩件事情。 第一,經過將store傳入root組件的context,使子節點能夠獲取到 state。 第二,經過store.subscribe 訂閱store的變化,更新組件。 另外還有對於性能的優化,減小沒必要要的渲染。git
首先咱們熟悉一下react-redux的基本使用github
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
const reducer = (state, action) => {
if (action.type === 'add') {
return {
...state,
count: state.count + 1,
}
}
return state;
};
const store = createStore(reducer, { count: 1 });
const mapStateToProps = (state) => {
return ({
count: state.count,
});
};
const mapDispatchToProps = dispatch => ({
add: () => dispatch({ type: 'add' }),
});
const mergeProps = (state, props) =>({
countStr: `計數: ${state.count}`,
...props,
});
const options = {
pure: true,
};
class App extends React.Component {
render() {
return (
<div> <p>{this.props.countStr}</p> <button onClick={this.props.add}>點擊+1</button> </div>
)
}
}
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
options,
)(App);
ReactDOM.render(
<Provider store={store}> <AppContainer /> </Provider>,
document.getElementById('root'));
複製代碼
從上面的例子中咱們能夠看出,react-redux使用很是簡單,僅僅使用了兩個API,Provider
與 connect
。redux
class Provider extends Component {
getChildContext() {
return { [storeKey]: this[storeKey], [subscriptionKey]: null }
}
constructor(props, context) {
super(props, context)
this[storeKey] = props.store;
}
render() {
return Children.only(this.props.children)
}
}
複製代碼
夠簡單吧,僅僅是把store放在了context下。subscriptionKey 能夠暫時不用管。緩存
首先咱們先回憶一下connect使用方法:性能優化
connect(mapStateToProps,mapDispatchToProps,mergeProps,options)(App);
複製代碼
connect源碼:框架
export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {}) {
return function connect( mapStateToProps, mapDispatchToProps, mergeProps, { pure = true, // ... } = {} ) {
// 封裝了傳入的mapStateToProps等函數,這裏能夠先理解爲 initMapStateToProps = () => mapStateToProps 這種形式
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
// 選擇器(selector)的做用就是計算mapStateToProps,mapDispatchToProps, ownProps(來自父組件的props)的結果,
// 並將結果傳給子組件props。這就是爲何你在mapStateToProps等三個函數中寫的結果,子組件能夠經過this.props拿到。
// 選擇器工廠函數(selectorFactory)做用就是建立selector
return connectHOC(selectorFactory, {
// 若是沒有傳,那麼將不會監聽state變化
shouldHandleStateChanges: Boolean(mapStateToProps),
// 傳給selectorFactory的參數
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
pure,
// ...
})
}
}
export default createConnect()
複製代碼
這段咱們能夠知道,connect是對connectHOC(connectAdvanced)的封裝,connectAdvanced使用了 defaultSelectorFactory,來建立selector。 react-redux 默認的 selectorFactory 中包含了不少性能優化的部分(咱們一下子會看到)。 其實react-redux 也提供了connectAdvanced API,爲了便於你們理解我經過改變開頭的例子,瞭解一下selectorFactory 是如何工做的。dom
// const AppContainer = connect(
// mapStateToProps,
// mapDispatchToProps,
// mergeProps,
// options,
// )(App);
// 在以前咱們使用了connect,如今咱們使用 connectAdvanced 來實現一下。
// 主要是是實現 selectorFactory:
function selectorFactory(dispatch) {
let result = {}
const action = mapDispatchToProps(dispatch);
return (nextState, nextOwnProps) => {
const state = mapStateToProps(nextState);
const nextResult = mergeProps(state, action, nextOwnProps);
if (!shallowEqual(result, nextResult)) result = nextResult
return result;
}
}
const AppContainer = connectAdvanced(selectorFactory)(App);
複製代碼
這是一個簡單的 selectorFactory,主要體現了其是如何工做的,讓你們有一個大概的瞭解。 下面來看一下react-redux是如何實現 selectorFactory 的ide
在解讀代碼以前,我想先說一下options.pure的做用。不知道你們還記得嗎,在開頭的例子有一個配置項pure。做用是減小運算優化性能。當設置爲false時,react-redux將不會優化,store.subscirbe事件觸發,組件就會渲染,即便是沒有用到的state更新,也會這樣,舉個例子
函數
// 你們都知道reducer的寫法
return {
...state,
count: state.count + 1,
}
// 必須返回一個新對象,那麼若是我只是修改原來 state 好比
state.count += 1;
return state;
// 你會發現組件將不會渲染,其實store裏的值是發生變化的。
// 這時若是你非想這麼寫, 而後又想從新渲染怎麼辦?
// 就是將pure設置爲false,這樣組件就會徹底實時更新。
複製代碼
舉了個不恰當的例子,你們千萬不要這麼幹。。。
源碼:
export default function finalPropsSelectorFactory(dispatch, { initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }) {
const mapStateToProps = initMapStateToProps(dispatch, options)
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
const mergeProps = initMergeProps(dispatch, options)
if (process.env.NODE_ENV !== 'production') {
verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName)
}
const selectorFactory = options.pure
? pureFinalPropsSelectorFactory
: impureFinalPropsSelectorFactory
return selectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
options
)
}
複製代碼
這裏很好理解,是對 selectorFactory 的封裝,根據 options.pure 的值,選取不一樣的 SelectorFactory;
先來看看簡單的 impureFinalPropsSelectorFactory
,其實和以前實現的 selectorFactory,差很少,無腦計算返回新值。
export function impureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch ) {
return function impureFinalPropsSelector(state, ownProps) {
return mergeProps(
mapStateToProps(state, ownProps),
mapDispatchToProps(dispatch, ownProps),
ownProps
)
}
}
複製代碼
怎麼樣,和以前本身實現的差很少吧,本身實現的還有一個淺對比呢~ 笑哭
pureFinalPropsSelectorFactory
export function pureFinalPropsSelectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch, { areStatesEqual, areOwnPropsEqual, areStatePropsEqual } ) {
let hasRunAtLeastOnce = false
let state
let ownProps
let stateProps
let dispatchProps
let mergedProps
function handleFirstCall(firstState, firstOwnProps) {
state = firstState
ownProps = firstOwnProps
stateProps = mapStateToProps(state, ownProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
hasRunAtLeastOnce = true
return mergedProps
}
function handleNewPropsAndNewState() {
stateProps = mapStateToProps(state, ownProps)
if (mapDispatchToProps.dependsOnOwnProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
function handleNewProps() {
// ...
}
function handleNewState() {
// ...
}
function handleSubsequentCalls(nextState, nextOwnProps) {
const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
const stateChanged = !areStatesEqual(nextState, state)
state = nextState
ownProps = nextOwnProps
if (propsChanged && stateChanged) return handleNewPropsAndNewState()
if (propsChanged) return handleNewProps()
if (stateChanged) return handleNewState()
return mergedProps
}
return function pureFinalPropsSelector(nextState, nextOwnProps) {
return hasRunAtLeastOnce
? handleSubsequentCalls(nextState, nextOwnProps)
: handleFirstCall(nextState, nextOwnProps)
}
}
複製代碼
一句話: 在須要的時候才執行mapStateToProps,mapDispatchToProps,mergeProps 爲了減小篇幅,挑部分講解。其實這篇已經很長了有沒有,不知道看到這裏的你,犯困了沒有? 仍是已經睡着了? 上個圖醒醒腦
言歸正傳,這裏也分兩種狀況:
其中 mapFunction.dependsOnOwnProps 表明你傳入的mapStateToProps是否使用了ownProps。 若是沒有使用,那麼props的變化將不會影響結果,換句話說對應的mapFunction將不會執行。 判斷方法也很簡單,就是獲取function形參長度,如何得到呢? mdn function.length
這邊文章主要講了react-redux使用方法以及分析了源碼中 Provider、connect、selectorFactory。中間也穿插了一些demo方便你們理解。 到目前爲止你們應該已經熟悉了整個框架的工做流程。因爲感受篇幅過長,因此決定分爲兩期來說解。下面一期中主要是剩下的 connectHOC(connectAdvanced),這個纔是react-redux的核心。 但願看完的朋友沒有浪費大家時間,有所幫助,有什麼意見儘管提,就當大家本身是產品(🐶)好le.
寫完又讀了一遍,感受篇幅其實不長,想一想應該是本身寫的累了。。。