react-redux用法及源碼解讀

react-redux把組件分爲UI組件和容器組件。先看圖下圖:

clipboard.png

下面讓咱們帶着問題學習一下react-redux:
1. react-redux如何將store分發到不一樣組件中?
2. react-redux如何作到store發生變化, 對應的組件從新render, 也就是說如何subscribe store?html

UI組件(純組件)

UI組件特徵:
  • 只負責渲染UI, 沒有邏輯。
  • 沒有狀態state。
  • 全部數據都由父組件傳遞, 也就是從props獲取。

容器組件

特徵:
  • 負責業務邏輯和數據管理。
  • 帶有內部狀態, 內部可使用state。
  • 使用redux的API。

react-redux API解釋

connect()
用於包裹UI組件生成容器組件, 也就是connect鏈接的意思。
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]);

// mapStateToProps(state, ownProps?): function, 返回的是Object(stateProps), 該方法將容器組件state映射到UI組件的props, 也就是輸入邏輯, 重點是mapStateToProps會訂閱store,每當store更新時, 會從新執行, 若是計算出的UI組件的參數發生變化, 就會觸發UI組件的從新渲染。state, 表明監聽的全局store,也能夠說是全局的state, ownProps表示該組件自身攜帶的props方法。

// mapDispatchToProps(dispatch, [ownProps]):function/Object, 若是返回的是對象,則對應每一個Action 的函數名, 在UI組件中調用須要dispatch, 若是返回的是函數,則已經被dispatch包裹(能夠用bindActionCreators)。 該參數主要是映射用戶動做Action, 從UI組件傳遞出去, 也就是輸出邏輯,這裏能夠用到redux的api bindActionCreators,

// mergeProps(stateProps, dispatchProps, ownProps) 指定傳入的UI組件的props, 默認是Object.assign({}, ownProps, stateProps, dispatchProps)

// options: Obejct, 能夠進一步定義connect功能, 如定義store的來源,指定props的一些更新規則等。


// action,js 
...
export const initSelectConcact = (streamConcacts: StreamToConcact): InitSelectConcactAction => ({
    type: LOCAL_CONCACT,
    streamConcacts,
});
export const testFetch = (dispatch) => dispatch(initSelectConcact([]));

// UnreadCards.js
class UnreadCards
...
componentDidMount() {
    this.props.fetchStreamConcact();
}

const mapStateToProps = (state, ownProps) => ({
  users: getUsers(state),
});
const mapDispatchToProps = (dispatch, ownProps) => ({
  fetchStreamConcact: () => testFetch(dispatch),  // 或者以下
  fetchStream: bindActionCreators(initSelectConcact, dispatch)
});
const mergeProps = (stateProps, dispatchProps, ownProps) => {
  console.log('mergeProps', stateProps, dispatchProps, ownProps);
  return Object.assign({}, ownProps, {
    todos: stateProps.todos[ownProps.userId],
    addTodo: (text) => dispatchProps.addTodo(ownProps.userId, text),
  });
};
const options = {
  pure: true,
  storeKey: 'store',
};

export default connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(UnreadCards);
Provider

connect生成容器組件後,須要將是state數據傳遞到子組件中, 有兩種方案:react

  1. A -> B -> C組件數據中傳遞, 能夠經過props一層層的傳遞。
  2. A -> B -> C利用React 的 context屬性, 將state綁在跟組件A的context中, A下面的全部子組件均可以經過context拿到是state。
    因此,Provider應用第二種, 將全局state綁定在跟組件的context上, 因此Provider的通常用法是在最頂層組件。

回答問題:

  • 1.react-redux是經過應用React的Context, 將store經過頂層組件傳遞到上下文環境中。源碼截圖以下:
    圖片描述
  • 2.通過上面的API的分析, 咱們瞭解connect主要是將咱們須要的(組件對應的全局state經過reducer定義的key,從全局state篩選出來)數據、將store和action對應, merge二者以props形式傳遞給connect包裹的組件, 而mapStateToProps內部監聽了store的變化, 每當store發生變化,對應映射的state也相應變化, 以props 發生變化而update render,看以下源碼:
    圖片描述
    圖片描述
    圖片描述

介紹一個高階組件connectWithActions

爲避免在connect參數mapDispatchToProps中反覆寫action, 我將全部Action經過方法綁定在props的actions上, 不用每次去映射當前頁面所需的action。redux

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

// 全部頁面action集合
import * as actions from './actions';

// 緩存actions, 不用每次render都從新加載
let cachedActions;

// action經過bindActionCreators綁定dispatch,
const bindActions = (dispatch, ownProps) => {
  if (!cachedActions) {
    cachedActions = {
      dispatch,
      actions: bindActionCreators(actions, dispatch),
    };
  }

  return cachedActions;
};

const connectWithActions = (mapStateToProps: MapStateToProps, mergeProps, options) => (
  component,
) => connect(mapStateToProps, bindActions, mergeProps, options)(component);

export default connectWithActions;


// 用法
class HomeScreen extennds Component {

    componentWillMount() {
        this.props.actions.getList();
    }
    ...

}
export default connectWithActions(state => ({
    users: state.users, 
}))(HomeScreen);

-----完-----api

參考文章緩存

相關文章
相關標籤/搜索