bindActionCreatorsがreact / reduxで使用されるのはいつですか?


91

bindActionCreatorsのReduxドキュメントには、次のように記載されています。

の唯一の使用例bindActionCreatorsは、一部のアクションクリエーターをReduxを認識しないコンポーネントに渡し、ディスパッチまたはReduxストアを渡さない場合です。

bindActionCreators使用/必要になる例は何ですか?

どのようなコンポーネントがReduxを認識しませんか?

両方のオプションの長所/短所は何ですか?

//actionCreator
import * as actionCreators from './actionCreators'

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch)
}

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return {
    someCallback: (postId, index) => {
      dispatch({
        type: 'REMOVE_COMMENT',
        postId,
        index
      })
    }
  }
}

回答:


58

私は最も一般的な答えは実際には問題に対処しているとは思わない。

以下のすべての例は、基本的に同じことを行い、「事前バインディング」なしの概念に従います。

// option 1
const mapDispatchToProps = (dispatch) => ({
  action: () => dispatch(action())
})


// option 2
const mapDispatchToProps = (dispatch) => ({
  action: bindActionCreators(action, dispatch)
})


// option 3
const mapDispatchToProps = {
  action: action
}

オプション#3はオプションの省略形にすぎない#1ので、なぜオプション#1vsオプションを使用するのかという本当の問題#2です。これらの両方がreact-reduxコードベースで使用されているのを見てきましたが、かなり混乱しています。

混乱は、 (質問自体で引用されている)bindActionCreatorsのドキュメントがreact-reduxで使用しないように言っているのに対して、ドキュメント内のすべてのreact-redux使用bindActionCreatorsしているという事実に 起因すると思います。

答えはコードベースの一貫性だと思いますが、私は個人的に必要なときにいつでもアクションをディスパッチに明示的にラップすることを好みます。


2
オプションはオプション#3の省略形#1ですか?
ogostos 2018年


@ArtemBernatskyiありがとう。だから、3例があるが判明mapDispatchToPropsfunctionobject行方不明に。どのようにそれぞれのケースを処理するためには、定義され、ここで
ogostos

私はこの答えを探していました。ありがとう。
Kishan Rajdev

Reactドキュメントが今話している特定のユースケースに実際に遭遇しました。「いくつかのアクション作成者を、Reduxを認識していないコンポーネントに渡します」というのは、単純なコンポーネントがより複雑なコンポーネントに接続されてオーバーヘッドが追加されているためです。のreduxconnectし、addDispatchToProps単純なコンポーネントに、私はちょうど1回のプロップダウンを渡すことができればやり過ぎと思われます。しかし、これまで、私は承知しているとほとんどのためのすべてのケースmapDispatchの小道具には、いずれかのオプションになります#1か、#3答えに言及
icc97

48

99%の確率connect()で、mapDispatchToPropsパラメーターの一部として、React-Redux 関数と共に使用されます。これはmapDispatch、提供する関数内で明示的に使用することも、オブジェクトの省略構文を使用してアクション作成者でいっぱいのオブジェクトをに渡す場合は自動的に使用することもできますconnect

アイデアは、アクションクリエーターを事前バインドすることで、渡したコンポーネントがconnect()接続されていることを技術的に「わからない」ということthis.props.someCallback()です。つまり、実行する必要があることを知っているだけです。一方、アクションクリエーターをバインドせずにを呼び出したthis.props.dispatch(someActionCreator())場合、コンポーネントは、props.dispatch存在することを期待しているため、接続されていることを「認識」します。

このトピックについての考えをブログ投稿Idiomatic Redux:アクションクリエイターを使用する理由に書きました


1
しかし、それは問題ない 'mapDispatchToProps'と接続されています。デバッグなどの他の多くのものの間で関数定義(TSまたはフロー)が失われるため、アクションクリエーターをバインドすることは実際には否定的/無意味なことのようです。私の新しいプロジェクトでは、これを使用することはなく、今まで問題はありませんでした。また、佐賀を使用して状態を保持します。また、redux関数を呼び出すだけの場合(ただし、クリックがうまくいく)、アクションクリエーターを「アタッチ」するよりもクリーンで、私の意見では、面倒で無意味です。ただし、スマート(通常は画面コンポーネント)は、プロップに対してのみ接続を使用できます。
Oliver Dixon

19

より完全な例として、接続するアクション作成者でいっぱいのオブジェクトを渡します。

import * as ProductActions from './ProductActions';

// component part
export function Product({ name, description }) {
    return <div>
        <button onClick={this.props.addProduct}>Add a product</button>
    </div>
}

// container part
function mapStateToProps(state) {
    return {...state};
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({
        ...ProductActions,
    }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Product);

これが答えになるはずです
Deen John

16

元の質問に答えてみます...

Smart&Dumbコンポーネント

最初の質問では、基本的になぜbindActionCreatorsそもそも必要なのか、どのようなコンポーネントがReduxを認識していないのかを尋ねます。

つまり、ここでの考え方は、コンポーネントをスマート(コンテナー)コンポーネントとダム(プレゼンテーション)コンポーネントに分割する必要があるということです。 ダムコンポーネントは、知る必要のあるベースで動作します。彼らの魂の仕事は、与えられたデータをHTMLにレンダリングすることであり、それ以上のものはありません。彼らは、アプリケーションの内部動作を認識してはなりません。それらは、アプリケーションのスキンディープフロントレイヤーと見なすことができます。

一方、スマートコンポーネントは一種の接着剤であり、ダムコンポーネント用のデータを準備し、HTMLレンダリングは行いません。

この種のアーキテクチャは、UIレイヤーとその下のデータレイヤーの間の疎結合を促進します。これにより、2つのレイヤーのいずれかを他のレイヤー(つまり、UIの新しいデザイン)に簡単に置き換えることができ、他のレイヤーが壊れることはありません。

あなたの質問に答える:ダムコンポーネントはRedux(または、データレイヤーの不必要な実装の詳細)を知らないようにする必要があります。将来的に別のコンポーネントに置き換える可能性があるためです。

この概念の詳細については、Reduxのマニュアルを参照してください。詳細については、Dan Abramovによるプレゼンテーションとコンテナコンポーネントを参照してください。

どちらの例が良いですか

2番目の質問は、与えられた例の利点/欠点についてでした。

最初の例アクション作成者は別個に定義されactionCreators、それらが他の場所で再利用することができる意味し、ファイル/モジュール。これは、アクションを定義する標準的な方法です。これにはデメリットはほとんどありません。

第二の例では、複数の欠点を有し、インラインアクション作成者を定義しています。

  • アクション作成者は再利用できません(明らかに)
  • 物事はより冗長で、読みにくくなります
  • アクションタイプはハードコーディングされています。constsレデューサーで参照できるように、アクションタイプを個別に定義することをお勧めします。これにより、入力ミスの可能性が減少します。
  • アクションクリエーターをインラインで定義することは、それらを使用する推奨/期待される方法に反します。これにより、コードを共有する予定がある場合に、コミュニティにとってコードが読みにくくなります。

2番目の例には、最初の例に比べ1つの利点があります-書く方が速いです!したがって、コードの計画がこれ以上ない場合は、問題ないかもしれません。

私は少し物事を明確にすることができたことを願っています...


2

の使用例の1つbindActionCreators()は、複数のアクションを1つの小道具として「マッピング」することです。

通常のディスパッチは次のようになります。

いくつかの一般的なユーザーアクションを小道具にマッピングします。

const mapStateToProps = (state: IAppState) => {
  return {
    // map state here
  }
}
const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    userLogin: () => {
      dispatch(login());
    },
    userEditEmail: () => {
      dispatch(editEmail());
    },
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

大規模なプロジェクトでは、各ディスパッチを個別にマッピングするのは手間がかかります。互いに関連する一連のアクションがある場合、これらのアクション組み合わせることができます。たとえば、さまざまなユーザー関連のアクションをすべて実行したユーザーアクションファイル。代わりに、私たちが使用することができ、別発送と各アクションの呼び出しのbindActionCreators()代わりにdispatch

bindActionCreators()を使用した複数のディスパッチ

関連するすべてのアクションをインポートします。それらはおそらくreduxストアの同じファイルにあります

import * as allUserActions from "./store/actions/user";

そして今、ディスパッチを使用する代わりにbindActionCreators()を使用してください

    const mapDispatchToProps = (dispatch: Dispatch) => {
      return {
           ...bindActionCreators(allUserActions, dispatch);
        },
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps, 
    (stateProps, dispatchProps, ownProps) => {
      return {
        ...stateProps,
        userAction: dispatchProps
        ownProps,
      }
    })(MyComponent);

これで、小道具userActionを使用してコンポーネント内のすべてのアクションを呼び出すことができます。

IE: userAction.login() userAction.editEmail() または this.props.userAction.login() this.props.userAction.editEmail()

注:bindActionCreators()を単一のプロップにマップする必要はありません。(に=> {return {}}マップされる 追加userAction)。を使用bindActionCreators()して、1つのファイルのすべてのアクションを個別の小道具としてマップすることもできます。しかし、それを行うと混乱を招く可能性があります。私は、各アクションまたは「アクショングループ」に明示的な名前を付けることを好みます。私はまたownProps、これらの「子の小道具」が何であるか、またはそれらがどこから来ているのかについてより説明的になるように名前を付けたいと思います。Redux + Reactを使用すると、すべての小道具が提供されている場所で少し混乱する可能性があるため、説明が多いほど良いでしょう。


2

を使用bindActionCreatorsすることで、複数のアクション関数をグループ化して、Redux(ダムコンポーネント)を認識しないコンポーネントに渡すことができます。

// actions.js

export const increment = () => ({
    type: 'INCREMENT'
})

export const decrement = () => ({
    type: 'DECREMENT'
})
// main.js
import { Component } from 'react'
import { bindActionCreators } from 'redux'
import * as Actions from './actions.js'
import Counter from './counter.js'

class Main extends Component {

  constructor(props) {
    super(props);
    const { dispatch } = props;
    this.boundActionCreators = bindActionCreators(Actions, dispatch)
  }

  render() {
    return (
      <Counter {...this.boundActionCreators} />
    )
  }
}
// counter.js
import { Component } from 'react'

export default Counter extends Component {
  render() {
    <div>
     <button onclick={() => this.props.increment()}
     <button onclick={() => this.props.decrement()}
    </div>
  }
}

1

bindActionsCreatorsについてもっと知りたい思っていたので、プロジェクトにどのように実装したかを示します。

// Actions.js
// Action Creator
const loginRequest = (username, password) => {
 return {
   type: 'LOGIN_REQUEST',
   username,
   password,
  }
}

const logoutRequest = () => {
 return {
   type: 'LOGOUT_REQUEST'
  }
}

export default { loginRequest, logoutRequest };

Reactコンポーネント内

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ActionCreators from './actions'

class App extends Component {
  componentDidMount() {
   // now you can access your action creators from props.
    this.props.loginRequest('username', 'password');
  }

  render() {
    return null;
  }
}

const mapStateToProps = () => null;

const mapDispatchToProps = dispatch => ({ ...bindActionCreators(ActionCreators, dispatch) });

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);

0

の良い使用例の1つbindActionCreatorsは、redux-saga-routinesを使用してredux-sagaと統合する場合です。例えば:

// routines.js
import { createRoutine } from "redux-saga-routines";
export const fetchPosts = createRoutine("FETCH_POSTS");
// Posts.js
import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { fetchPosts } from "routines";

class Posts extends React.Component {
  componentDidMount() {
    const { fetchPosts } = this.props;
    fetchPosts();
  }

  render() {
    const { posts } = this.props;
    return (
      <ul>
        {posts.map((post, i) => (
          <li key={i}>{post}</li>
        ))}
      </ul>
    );
  }
}

const mapStateToProps = ({ posts }) => ({ posts });
const mapDispatchToProps = dispatch => ({
  ...bindActionCreators({ fetchPosts }, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Posts);
// reducers.js
import { fetchPosts } from "routines";

const initialState = [];

export const posts = (state = initialState, { type, payload }) => {
  switch (type) {
    case fetchPosts.SUCCESS:
      return payload.data;
    default:
      return state;
  }
};
// api.js
import axios from "axios";

export const JSON_OPTS = { headers: { Accept: "application/json" } };
export const GET = (url, opts) =>
  axios.get(url, opts).then(({ data, headers }) => ({ data, headers }));
// sagas.js
import { GET, JSON_OPTS } from "api";
import { fetchPosts } from "routines";
import { call, put, takeLatest } from "redux-saga/effects";

export function* fetchPostsSaga() {
  try {
    yield put(fetchPosts.request());
    const { data } = yield call(GET, "/api/posts", JSON_OPTS);
    yield put(fetchPosts.success(data));
  } catch (error) {
    if (error.response) {
      const { status, data } = error.response;
      yield put(fetchPosts.failure({ status, data }));
    } else {
      yield put(fetchPosts.failure(error.message));
    }
  } finally {
    yield put(fetchPosts.fulfill());
  }
}

export function* fetchPostsRequestSaga() {
  yield takeLatest(fetchPosts.TRIGGER, fetchPostsSaga);
}

このパターンは、React Hooksを使用して実装できることに注意してください(React 16.8以降)。


-1

ドキュメントの文は非常に明確です:

の唯一の使用例bindActionCreatorsは、一部のアクションクリエーターをReduxを認識しないコンポーネントに渡し、ディスパッチまたはReduxストアを渡さない場合です。

これは明らかに、次の1つの条件でのみ発生する可能性があるユースケースです。

たとえば、コンポーネントAとBがあるとします。

// A use connect and updates the redux store
const A = props => {}
export default connect()(A)

// B doesn't use connect therefore it does not know about the redux store.
const B = props => {}
export default B

反応還元に注入:(A)

const boundActionCreators = bindActionCreators(SomeActionCreator, dispatch)
// myActionCreatorMethod,
// myActionCreatorMethod2,
// myActionCreatorMethod3,

// when we want to dispatch
const action = SomeActionCreator.myActionCreatorMethod('My updates')
dispatch(action)

react-reduxによって注入:(B)

const { myActionCreatorMethod } = props
<B myActionCreatorMethod={myActionCreatorMethod} {...boundActionCreators} />

次のことに気付きましたか?

  • コンポーネントBのreduxストアは不明でしたが、コンポーネントAを通じてreduxストアを更新しました。

  • コンポーネントAでは更新していません。正確に何を意味するかについては、この投稿をご覧ください。私はあなたがアイデアを持っていると思います。


7
何も理解できませんでした
vsync 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.