React-ReduxとmapStateToProps()を理解する


219

私はreact-reduxの接続方法と、それがパラメーターとして取る関数を理解しようとしています。特にmapStateToProps()

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

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

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


11
私はミックスに別の答えを追加したくありません...しかし、実際には誰もあなたの質問に答えないことに気づきます...私の意見では、それはアンチパターンではありません。キーは名前mapStateTo Propsにあり、コンポーネントが使用する読み取り専用プロパティを渡します。コンテナーコンポーネントを使用して状態を取得し、プレゼンテーションコンポーネントに渡す前に状態を変更することがよくあります。
マシューブレント2017

3
この方法では、私のプレゼンテーションコンポーネントがはるかに簡単です...私は、レンダリングされるかもしれないthis.props.someDataとは対照的に、this.props.someKey[someOtherKey].someData...メイク感覚?
マシューブレント2017

3
このチュートリアルはそれを十分に説明しています:learn.co/lessons/map-state-to-props-readme
Ayan

こんにちはパブロ、あなたの選んだ答えを再考してください。
vsync 2018

どのように再考しますか?
パブロバリアウレンダ

回答:


56

Q:Is this ok?
A:はい

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

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

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

これにより、コンポーネントを状態から切り離して、コードのモジュール性を向上させることができます。また、コンポーネントの状態をアプリケーションの状態のサブセットとして簡略化することもできます。これにより、実際にはReduxパターンに準拠することができます。

このように考えてください。ストアは アプリケーションの状態全体を含むことになっています。
大規模なアプリケーションの場合、これには何十ものプロパティが含まれ、多くの層が深くネストされます。
通話ごとにそのすべてを運ばないでください(高価です)。

なし、mapStateToPropsまたはその類似物がないと、パフォーマンスを改善/単純化する別の方法で状態を切り分けたくなるでしょう。


6
どんなに大きくても、すべてのコンポーネントがストア全体にアクセスできるようにすることは、パフォーマンスとは関係がないと思います。オブジェクトの受け渡しは常に同じオブジェクトであるため、メモリを消費しません。コンポーネントに必要なパーツをコンポーネントにもたらす唯一の理由は、おそらく2つの理由です:(1) -より深いアクセスが容易(2) -コンポーネントがそれに属していない状態を台無しにする可能性があるバグを回避する
vsync

@vsyncこれにより、ディープアクセスが容易になる方法を説明してください グローバル状態を参照する代わりにローカルプロップを使用できるようになり、読みやすくなりましたか?
シッダールタ

また、状態が不変として渡されたときに、コンポーネントがそれに属していない状態をどのように台無しにすることができますか?
シッダールタ

状態が不変の場合はそれで問題ないと思いますが、それでも、適切な方法として、コンポーネントに関連する部分のみをコンポーネントに公開することをお勧めします。これは、他の開発者が(状態オブジェクトの)その部分に関連する部分をよりよく理解するのにも役立ちます。「より簡単なアクセス」に関しては、ある深い状態へのパスが直接小道具としてコンポーネントに渡され、そのコンポーネントが舞台裏でReduxにあるという事実を知らないという意味で簡単です。コンポーネントは、どの状態管理システムが使用されているかを気にする必要はなく、受け取った小道具でのみ機能する必要があります。
vsync

119

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

postsアプリにキーがあると想像してくださいstate.posts

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

そしてコンポーネント Posts

デフォルトでconnect()(Posts)は、すべての状態の小道具が接続されたコンポーネントで利用できるようになります

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;
  }, {})
}

何かを見逃しているかもしれませんdispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)が、fetchPostsdeletePostアクションはどこから渡されますか?
ilyo 2017年

@ilyoこれらはあなたのアクションクリエーターです。インポートする必要があります
webdeb

2
素敵な答え!このコードのチャンクstate => state.postsmapStateToProps関数)が、更新時にコンポーネントの再レンダリングをトリガーする状態をReactに通知することを強調するのも良いと思います。
MiguelPéres2017

38

最初の部分は正しいです:

はい。mapStateToProps引数/パラメータ(によって提供react-redux::connect)としてストア状態があり、コンポーネントをストア状態の特定の部分にリンクするために使用されます。

リンクとは、mapStateToProps構築時に返されるオブジェクトが小道具として提供され、その後の変更がを通じて利用できることを意味しますcomponentWillReceiveProps

オブザーバーの設計パターンを知っている場合は、それまたはそのわずかなバリエーションです。

例は物事をより明確にするのに役立ちます:

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);

itemsFilters表示を処理し、フィルターの状態をRedux Store状態に維持する別のreactコンポーネントが呼び出される可能性があります。DemoコンポーネントはRedux Store状態フィルターを「リッスン」または「サブスクライブ」するため、フィルターは(の助けを借りてfiltersComponent)状態の変更を保存するたびに反応します-reduxは、変更があったことを検出し、変更を送信することで、すべてのリスニング/サブスクライブコンポーネントに通知または「公開」します。componentWillReceivePropsこの例では、アイテムの再フィルターをトリガーし、反応状態が変化したために表示を更新します。 。

例がわかりにくいか、わかりやすく説明していないかを教えてください。

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

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

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

Reduxストアの状態はReduxレデューサーの状態の組み合わせであり、それぞれが小さな部分のアプリロジックを管理します。これらのレデューサー属性にはreact-redux::connect@mapStateToProps、コンポーネントを使用してアクセスできます。これにより、コンポーネントの状態がそれ自体に排他的である一方で、Reduxストアの状態にアクセスできるアプリが広くなります。


5

この反応還元の例は、Mohamed Melloukiの例に基づいています。ただし、prettifyおよびlinting ルールを使用して検証しますPropTypesを使用して小道具とディスパッチメソッドを定義しているので、コンパイラーが悲鳴を上げないことに注意してください。この例には、Mohamedの例では欠落していたいくつかのコード行も含まれています。connectを使用するには、react-reduxからインポートする必要があります。この例で、メソッドfilterItems もバインドしています。これにより、コンポーネントのスコープの問題が回避されます。このソースコードは、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);

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


2

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

import { connect } from 'react-redux';

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

export default AppContainer;

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

Redux接続を理解するには、githubプロジェクトのクローンを作成するか、そのブログからコードをコピーして貼り付けます。


良いマニュアルformapStateToPropsは thegreatcodeadventure.com/...
zloctb

1

以下は、の動作を説明するための概要/ボイラープレートです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} />;
    }
  }
}

-2
import React from 'react';
import {connect} from 'react-redux';
import Userlist from './Userlist';

class Userdetails extends React.Component{

render(){
    return(
        <div>
            <p>Name : <span>{this.props.user.name}</span></p>
            <p>ID : <span>{this.props.user.id}</span></p>
            <p>Working : <span>{this.props.user.Working}</span></p>
            <p>Age : <span>{this.props.user.age}</span></p>
        </div>
    );
 }

}

 function mapStateToProps(state){  
  return {
    user:state.activeUser  
}

}

  export default connect(mapStateToProps, null)(Userdetails);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.