了解React-Redux和mapStateToProps()


219

我正在尝试了解react-redux的connect方法及其作为参数的功能。特别是mapStateToProps()

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

这意味着目标组件所消耗的状态与存储在商店中的状态可能具有截然不同的结构。

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


11
我不想在组合中添加其他答案...但是我意识到没有人真正回答您的问题...我认为这不是反模式。关键在于名称mapStateTo Props中,您正在传递只读属性以供组件使用。我经常会使用容器组件获取状态并进行更改,然后再将其传递给表示组件。
马修·布伦特

3
这样,我的呈现组件就更简单了……我可能正在渲染this.props.someData而不是this.props.someKey[someOtherKey].someData……有意义吗?
马修·布伦特

3
本教程对此进行了很好的解释:learn.co/lessons/map-state-to-props-readme
Ayan

嗨,Pablo,请重新考虑您选择的答案。
vsync

重新考虑如何?
PabloBarríaUrenda '18

Answers:


56

问:Is this ok?
答:是的

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

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

这称为“连接”组件或“使其智能化”。这是设计使然。

它允许您额外花费时间使组件与状态分离,从而增加了代码的模块化。它还允许您将组件状态简化为应用程序状态的子集,实际上,这有助于您遵循Redux模式。

这样考虑:存储应该包含 应用程序的整个状态。
对于大型应用程序,它可能包含数十个嵌套在多层中的属性。
您不想每次通话都花很多钱(昂贵)。

没有mapStateToProps或类似的东西,您很想用另一种方式来提高状态/提高性能/简化。


6
我不认为让每个组件都可以访问整个商店,无论它有多大,都与性能无关。传递对象不会占用内存,因为它始终是同一对象。组件所需的零件的唯一原因可能是两个原因:(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

@ilyo这些是您的动作创建者,您必须导入它们
webdeb

2
好答案!我认为还应该强调,这段代码state => state.postsmapStateToProps函数)将告诉React什么状态将在更新时触发组件的重新渲染。
米格尔·佩雷斯(MiguelPéres)

38

您正确地掌握了第一部分:

mapStateToProps,将存储状态作为参数/参数(由提供react-redux::connect),用于将组件与存储状态的特定部分链接。

通过链接我的意思是所返回的对象mapStateToProps将在施工时作为道具提供,任何后续的变化都可以通过获取componentWillReceiveProps

如果您知道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);

可以有另一个React组件itemsFilters来处理显示并将过滤器状态保持为Redux Store状态,Demo组件正在“监听”或“订阅” Redux Store状态过滤器,因此只要过滤器存储状态发生变化(借助filtersComponent),都会做出反应-redux检测到有更改,并通过将更改发送到侦听/预订的组件来通知或“发布”所有侦听/预订的组件componentWillReceiveProps,在此示例中,由于反应状态已更改,该将触发项目的重新过滤并刷新显示。

让我知道该示例是否令人困惑或不够清晰,无法提供更好的解释。

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

我没有得到这个问题,但是只知道react状态(this.setState)与Redux Store状态完全不同!

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

Redux Store状态是Redux reducer状态的组合,每个状态负责管理一小部分应用程序逻辑。可以react-redux::connect@mapStateToProps通过任何组件的帮助来访问那些reducers属性!这使得Redux存储状态可在应用程序中广泛访问,而组件状态是其专有的。


5

reactredux示例基于Mohamed Mellouki的示例。但是使用美化掉毛规则进行验证。请注意,我们使用PropTypes定义了props和dispatch方法,以便我们的编译器不会对我们尖叫。该示例还包括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;

在此博客中对此进行了非常简单和清晰的解释。

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


良好的手动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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.