mapStateToProps で Props にマップした値を mapDispatchToProps 内で利用したい人生だった

例えば、アクセストークンを Redux のストアに保存していて、そのアクセストークンを使って Web API を呼び出したい。 アクセストークンを受け取って Web API を呼び出すアクションおよび ActionCreator は既にあるとする。

Redux のストアで管理しているステートからコンポーネントの Props にアクセストークンを渡すのは、mapStateToProps でやればいい。 Web API を呼び出す ActionCreator を dispatch する部分は、mapDispatchToProps で書きたいところ。 ただ、mapDispatchToProps 内では mapStateToProps で Props にマップした値を取得できない。 気を利かせて、ownProps に渡してくれたらよかったのに。 さて、どうしよう。

救世主は、connect が受け取る 3 番目の引数、mergeProps だった。 mapStateToProps が返したオブジェクトである stateProps、mapDispatchToProps が返したオブジェクトである dispatchProps、 そして ownProps を受け取り、3 つをマージしたオブジェクトを返す。 mergeProps 内なら、mapStateToProps でマップしたアクセストークンが stateProps から取得できるので、 そのアクセストークンを使って Web API を呼び出すアクションが dispatch できる。

こんな感じで。コードは TypeScript。

import React from 'react';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';

import { AppState } from './reducers';
import { callWebApi } from './actions';

interface IndexPageProps {
  dispatch: Dispatch;
  token: string;
  onLoad: () => void;
}

class IndexPage extends React.Component<IndexPageProps> {
  componentDidMount() {
    this.props.onLoad();
  }

  render() {
    return (/* 本題ではないので省略 */);
  }
}

// アクセストークンを Props にマップ
function mapStateToProps(
  state: AppState,
  ownProps: IndexPageProps
): IndexPageProps {
  return {
    ...ownProps,
    token: state.login.token,
  };
}

// mapStateToProps でマップした値を使うアクションを dispatch する処理は、
// mapDispatchToProps で書かないでおく
function mapDispatchToProps(
  dispatch: Dispatch,
  ownProps: IndexPageProps
): IndexPageProps {
  return {
    ...ownProps,
    dispatch,
  };
}

function mergeProps(
  stateProps: IndexPageProps,
  dispatchProps: IndexPageProps,
  ownProps: IndexPageProps
): IndexPageProps {
  return {
    ...ownProps,
    ...stateProps,
    ...dispatchProps,

    // mapDispatchToProps ではなく mergeProps で実装する
    onLoad: () => {
        // mapStateToProps で props にマップしたものは
        // stateProps から取得できる
        const token = stateProps.token;

        // mapDispatchToProps で props にマップしたものは
        // dispatchProps から取得できる
        const dispatch = dispatchProps.dispatch;

        // mapStateToProps でマップしたデータを使って
        // アクションを dispatch できる
        dispatch(callWebAPI(token));
    },
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(IndexPage);