理解React-Redux和mapStateToProps()

时间:2016-07-05 11:42:41

标签: javascript redux react-redux

我试图了解react-redux的连接方法,以及它作为参数所采用的函数。特别是mapStateToProps()

我理解它的方式,mapStateToProps的返回值将是一个从状态派生的对象(因为它存在于商店中),其键将被传递给目标组件(组件连接应用于)作为道具。

这意味着目标组件所使用的状态与存储在商店中的状态可能具有完全不同的结构。

问:这可以吗? 问:这是预期的吗? 问:这是一种反模式吗?

10 个答案:

答案 0 :(得分:107)

是的,这是正确的。它只是一个辅助函数,可以更简单地访问您的状态属性

想象一下,您的应用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)

现在您可以在Component

中使用它
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;
  }, {})
}

答案 1 :(得分:42)

问:Is this ok?
答:是的

问:Is this expected?
是的,这是预期的(如果您使用react-redux)。

问:Is this an anti-pattern?
答:不,这不是反模式。

它被称为“连接”您的组件或“使其变得聪明”。这是设计的。

它允许您将组件与状态分离一段时间,从而增加代码的模块性。它还允许您将组件状态简化为应用程序状态的子集,这实际上可以帮助您遵守Redux模式。

以这种方式思考:商店应该包含应用程序的整个状态。
对于大型应用程序,这可能包含许多嵌套多层深层的属性 你不想在每次通话时都花费这么多(昂贵的)。

如果没有mapStateToProps或其中的某些类似物,您很可能会以另一种方式来改变您的状态以提高性能/简化。

答案 2 :(得分:31)

你的第一部分是正确的:

IDatabaseClient将Store状态作为参数/ param(由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); 的反应组件来处理显示并将过滤器状态保持在Redux Store状态,Demo组件是&#34; listen&#34;或&#34;订阅&#34;到Redux Store状态过滤器,以便每当过滤器存储状态更改时(在itemsFilters的帮助下)react-redux检测到存在更改并通知或&#34;发布&#34;所有监听/订阅组件都通过将更改发送到他们的filtersComponent,在此示例中将触发项目的重新过滤器并刷新显示,因为反应状态已发生变化。

让我知道这个例子是否令人困惑或不够清楚,无法提供更好的解释。

至于:这意味着目标组件所使用的状态可能与存储在商店中的状态有着截然不同的结构。

我没有得到问题,但只知道反应状态(componentWillReceiveProps)与Redux Store状态完全不同!

反应状态用于处理反应组分的重绘和行为。反应状态仅包含在组件中。

Redux Store状态是Redux reducer状态的组合,每个状态都负责管理一小部分app逻辑。任何组件都可以在this.setState的帮助下访问这些reducers属性!这使得Redux存储状态可以访问应用程序范围,而组件状态是独有的。

答案 3 :(得分:5)

react&amp; redux示例基于Mohamed Mellouki的例子。 但是使用prettifylinting rules进行验证。请注意,我们定义了道具 和dispatch使用PropTypes的方法,以便我们的编译器不会尖叫我们。 这个例子还包括Mohamed的一些代码行 例。要使用连接,您需要从react-redux导入它。这个 示例还binds方法filterItems这将阻止scope问题 component。此源代码已使用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);

此示例代码是组件起始位置的良好模板。

答案 4 :(得分:2)

React-Redux connect用于为每个操作更新商店。

import { connect } from 'react-redux';

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

export default AppContainer;

这个blog非常简单明了地解释了。

您可以克隆github项目或复制粘贴该博客中的代码以了解Redux连接。

答案 5 :(得分:1)

这是一个简单的概念。 Redux根据化简器中的操作创建了一个普遍存在的状态对象(存储)。像React组件一样,此状态不必在任何地方进行显式编码,但它可以帮助开发人员在reducer文件中查看默认状态对象,以可视化正在发生的事情。您将reducer导入组件中以访问文件。然后,mapStateToProps仅在其组件所需的存储中选择键/值对。可以像Redux一样创建一个React组件的全局版本

this.state = ({ 
cats = [], 
dogs = []
})

使用mapStateToProps()无法更改状态的结构。您要做的是仅选择组件所需的商店的键/值对,然后将值(从商店中的键/值列表)传递到组件中的prop(本地键)。您一次在列表中执行此一个值。在此过程中不会发生结构更改。

P.S。商店是本地状态。在操作创建者参与进来的过程中,Reducer通常还会将状态传递给数据库,但是对于这种特定的发布,首先要了解这个简单的概念。

P.P.S。优良作法是将reducer分别分成单独的文件,然后仅导入组件所需的reducer。

答案 6 :(得分:0)

用于描述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} />;
    }
  }
}

答案 7 :(得分:0)

我想重新构建您提到的语句:

这意味着目标组件消耗的状态可以 与存储在状态上的状态有着截然不同的结构 您的商店

您可以说目标组件消耗的状态只占Redux存储中存储状态的一小部分。换句话说,组件消耗的状态将是redux存储状态的子集。

就了解connect()方法而言,这非常简单! connect()方法可以向组件添加新道具,甚至覆盖现有道具。通过此connect方法,我们还可以访问Provider抛出给我们的redux存储的状态。结合使用这两种方法对您有利,您可以将redux存储的状态添加到组件的props中。

以上是一些理论,建议您一次看一下video,以更好地理解语法。

答案 8 :(得分:0)

是的,您可以这样做。您甚至还可以处理状态并返回对象。

function mapStateToProps(state){  
  let completed = someFunction (state);
   return {
     completed : completed,  
   }
}
 

如果要将状态相关的逻辑从渲染功能转移到其外部,这将很有用。

答案 9 :(得分:-1)

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){  *state is your redux-store object* 
  return {
    user:state.activeUser  
}

}

  export default connect(mapStateToProps)(Userdetails);