web-dev-qa-db-ja.com

React-ReduxとmapStateToProps()について

私はreact-reduxのconnectメソッドとそれがパラメータとして取る関数を理解しようとしています。特にmapStateToProps()

私が理解しているように、mapStateToPropsの戻り値はstateから派生したオブジェクトになり(ストア内に存在する)、そのキーは小道具としてターゲットコンポーネントに渡されます(コンポーネントconnectが適用される)。

つまり、ターゲットコンポーネントによって消費される状態は、ストアに格納されている状態とはまったく異なる構造を持つ可能性があります。

Q:これで大丈夫ですか?
Q:これは予想されますか?
Q:これはアンチパターンですか?

178

Q:Is this ok?
A:はい

Q:Is this expected?
はい、これは予想されます(react-reduxを使用している場合)。

Q:Is this an anti-pattern?
A:いいえ、これはアンチパターンではありません。

それはあなたのコンポーネントを「接続する」あるいは「それをスマートにする」と呼ばれます。仕様によるものです。

それはあなたがあなたの状態からあなたのコンポーネントを切り離すことを可能にしますそれはあなたのコードのモジュール性を増加させます。それはまたあなたがあなたのアプリケーション状態のサブセットとしてあなたのコンポーネント状態を単純化することを可能にし、それは実際にはReduxパターンに従うのを助けます。

このように考えてみてください。ストアにはアプリケーションの 全体 状態が含まれているはずです。
大規模なアプリケーションでは、これには何層にも及ぶプロパティが含まれている可能性があります。
あなたはそれぞれの呼び出しの周りにそれらすべてを引っ張りたくない(高価)。

mapStateToPropsやそれに類するものがないと、パフォーマンスを向上させたり単純化したりするために自分の状態を別の方法で切り開こうと思うでしょう。

40

はい、それは正しいです。状態プロパティにアクセスするためのより簡単な方法を持つための、単なるヘルパー関数です。

あなたのアプリケーションにpostsキーがあると想像してくださいstate.posts

state.posts //
/*    
{
  currentPostId: "",
  isFetching: false,
  allPosts: {}
}
*/

そしてコンポーネントPosts

デフォルトではconnect()(Posts)はすべての状態の小道具を接続されたComponentに対して利用可能にします

const Posts = ({posts}) => (
  <div>
    {/* access posts.isFetching, access posts.allPosts */}
  </div> 
)

state.postsをあなたのコンポーネントにマッピングすると、少し良くなります。

const Posts = ({isFetching, allPosts}) => (
  <div>
    {/* access isFetching, allPosts directly */}
  </div> 
)

connect(
  state => state.posts
)(Posts)

mapDispatchToProps

通常はdispatch(anActionCreator())を書く必要があります

bindActionCreatorsを使えば、次のようにも簡単にできます。

connect(
  state => state.posts,
  dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)

今すぐあなたのコンポーネントでそれを使用することができます

const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
  <div>
    <button onClick={() => fetchPosts()} />Fetch posts</button>
    {/* access isFetching, allPosts directly */}
  </div> 
)

ActionCreatorsを更新します。

ActionCreatorの例:deletePost

const deletePostAction = (id) => ({
  action: 'DELETE_POST',
  payload: { id },
})

そのため、bindActionCreatorsは単にあなたの行動を取り、それらをdispatch呼び出しにラップします。 (reduxのソースコードは読みませんでしたが、実装は次のようになります。

const bindActionCreators = (actions, dispatch) => {
  return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
    actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
    return actionsMap;
  }, {})
}
107
webdeb

あなたは最初の部分を正しく得ました:

はいmapStateToPropsname__は、Store状態を引数/ param(react-redux::connectによって提供される)として保持し、コンポーネントをストア状態の特定の部分にリンクするために使用されます。

リンクすることによって、私はmapStateToPropsname__によって返されたオブジェクトが構築時に小道具として提供され、その後の変更はcomponentWillReceivePropsname__を通じて利用可能になることを意味します。

Observerのデザインパターンを知っていれば、それはまさにそれ、またはそれのわずかなバリエーションです。

例を挙げれば、物事がより明確になります。

import React, {
    Component,
} from 'react-native';

class ItemsContainer extends Component {
    constructor(props) {
        super(props);

        this.state = {
            items: props.items, //provided by connect@mapStateToProps
            filteredItems: this.filterItems(props.items, props.filters),
        };
    }

    componentWillReceiveProps(nextProps) {
        this.setState({
            filteredItems: this.filterItems(this.state.items, nextProps.filters),
        });
    }

    filterItems = (items, filters) => { /* return filtered list */ }

    render() {
        return (
            <View>
                // display the filtered items
            </View>
        );
    }
}

module.exports = connect(
    //mapStateToProps,
    (state) => ({
        items: state.App.Items.List,
        filters: state.App.Items.Filters,
        //the State.App & state.App.Items.List/Filters are reducers used as an example.
    })
    // mapDispatchToProps,  that's another subject
)(ItemsContainer);

表示を処理し、フィルタ状態をRedux Store状態に永続化するitemsFiltersname__という別の反応コンポーネントがある可能性があります。Demoコンポーネントは、(filtersComponentname__を使用して)フィルタ状態を変更するたびに「待機」または「購読」しますreact-reduxは変更があったことを検出し、その変更をcomponentWillReceivePropsname__に送信することによってすべてのリスニング/サブスクライブ・コンポーネントに通知または「発行」します。この例では、項目のリフィルをトリガーし、反応するという事実により表示を更新します状態が変わりました。

例が混乱を招くものであるか、またはより良い説明を提供するのに十分明確でないかどうかを私に知らせてください。

これは、ターゲットコンポーネントによって消費される状態が、ストアに格納されている状態とはまったく異なる構造を持つことができることを意味します。

私は質問を受けませんでしたが、反応状態(this.setState)がRedux Storeの状態とまったく異なることを知っているだけです!

反応状態は、反応コンポーネントの再描画と動作を処理するために使用されます。反応状態はコンポーネントに排他的に含まれます。

Reduxストア状態はReduxリデューサー状態の組み合わせであり、それぞれが小部分のアプリロジックの管理を担当します。これらのリデューサーの属性はどのコンポーネントからでもreact-redux::connect@mapStateToPropsの助けを借りてアクセスすることができます!コンポーネントの状態はそれ自身に排他的であるのに対し、Reduxストアの状態はアプリ全体にアクセス可能にします。

31

この reactredux の例は、Mohamed Melloukiの例に基づいています。しかし、 prettify および リンティング規則 を使用して検証します。私たちのコンパイラーが私たちに叫ぶことがないように、私たちは PropTypes を使って私たちの小道具と dispatch メソッドを定義することに注意してください。この例には、Mohamedの例に欠けていたいくつかのコード行も含まれています。 connectを使うには、 react-redux からインポートする必要があります。この例はまた、 bind メソッドfilterItems scopecomponent の問題を防ぐことを可能にします。このソースコードはJavaScript Prettify を使って自動フォーマットされています。

import React, { Component } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

class ItemsContainer extends Component {
  constructor(props) {
    super(props);
    const { items, filters } = props;
    this.state = {
      items,
      filteredItems: filterItems(items, filters),
    };
    this.filterItems = this.filterItems.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const { itmes } = this.state;
    const { filters } = nextProps;
    this.setState({ filteredItems: filterItems(items, filters) });
  }

  filterItems = (items, filters) => {
    /* return filtered list */
  };

  render() {
    return <View>/*display the filtered items */</View>;
  }
}

/*
define dispatch methods in propTypes so that they are validated.
*/
ItemsContainer.propTypes = {
  items: PropTypes.array.isRequired,
  filters: PropTypes.array.isRequired,
  onMyAction: PropTypes.func.isRequired,
};

/*
map state to props
*/
const mapStateToProps = state => ({
  items: state.App.Items.List,
  filters: state.App.Items.Filters,
});

/*
connect dispatch to props so that you can call the methods from the active props scope.
The defined method `onMyAction` can be called in the scope of the componets props.
*/
const mapDispatchToProps = dispatch => ({
  onMyAction: value => {
    dispatch(() => console.log(`${value}`));
  },
});

/* clean way of setting up the connect. */
export default connect(mapStateToProps, mapDispatchToProps)(ItemsContainer);

このサンプルコードは、コンポーネントの出発点として最適なテンプレートです。

5

React-Redux connectはすべてのアクションのストアを更新するために使用されます。

import { connect } from 'react-redux';

const AppContainer = connect(  
  mapStateToProps,
  mapDispatchToProps
)(App);

export default AppContainer;

これは非常に単純かつ明確にこの ブログ で説明されています。

Githubプロジェクトを複製するか、そのブログのコードをコピーしてRedux connectを理解することができます。

2
ArunValaven

mapstateToPropsを記述するためのボアプレート:

//これはReduxコンテナが行うことを非常に単純化した実装です。

class MyComponentContainer extends Component {
  mapStateToProps(state) {
    // this function is specific to this particular container
    return state.foo.bar;
  }

  render() {
    // This is how you get the current state from Redux,
    // and would be identical, no mater what mapStateToProps does
    const { state } = this.context.store.getState();

    const props = this.mapStateToProps(state);

    return <MyComponent {...this.props} {...props} />;
  }
}

そして次に

function buildReduxContainer(ChildComponentClass, mapStateToProps) {
  return class Container extends Component {
    render() {
      const { state } = this.context.store.getState();

      const props = mapStateToProps(state);

      return <ChildComponentClass {...this.props} {...props} />;
    }
  }
}
0
zloctb