【问题标题】:Understanding React-Redux and mapStateToProps()了解 React-Redux 和 mapStateToProps()
【发布时间】:2016-11-07 05:45:25
【问题描述】:

我试图理解 react-redux 的 connect 方法,以及它作为参数的函数。特别是mapStateToProps()

按照我的理解,mapStateToProps 的返回值将是从状态派生的对象(因为它存在于存储中),其键将传递给您的目标组件(组件连接应用于)为道具。

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

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

【问题讨论】:

  • 我不想再添加另一个答案...但我意识到没有人真正回答您的问题...在我看来,它不是反-图案。关键在于名称 mapStateToProps 您正在传递只读属性以供组件使用。我会经常使用我的容器组件来获取状态并在将其传递给演示组件之前对其进行更改。
  • 这样我的展示组件就简单多了...我可能会渲染this.props.someData而不是this.props.someKey[someOtherKey].someData...有意义吗?
  • 本教程解释得很好:learn.co/lessons/map-state-to-props-readme
  • 嗨,Pablo,请重新考虑您选择的答案。
  • 重新考虑如何?

标签: javascript reactjs redux react-redux


【解决方案1】:

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

假设您的应用程序中有一个postsstate.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)

ma​​pDispatchToProps

通常你必须写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 =&gt; bindActionCreators({fetchPosts, deletePost}, dispatch) 是从哪里获得fetchPostsdeletePost 的操作传递过来的?
  • @ilyo 这些是你的动作创建者,你必须导入它们
  • 不错的答案!我认为也很高兴强调这段代码state =&gt; state.postsmapStateToProps 函数)将告诉 React 更新时哪些状态将触发组件的重新渲染。
【解决方案2】:

问:Is this ok?
答:是的

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

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

这被称为“连接”您的组件或“使其变得智能”。这是设计使然。

它允许您将组件与状态解耦,从而增加代码的模块化。它还允许您将组件状态简化为应用程序状态的子集,这实际上有助于您遵守 Redux 模式。

这样想:商店应该包含应用程序的整个状态。
对于大型应用程序,这可能包含数十个嵌套很多层的属性。
您不想在每次通话时都拖拉所有这些(昂贵)。

如果没有mapStateToProps 或其类似物,您会很想通过另一种方式来划分您的状态以提高性能/简化。

【讨论】:

  • 我不认为让每个组件都可以访问整个商店,无论它有多大,都与性能有任何关系。传递对象不会占用内存,因为它始终是 same 对象。为组件带来所需部件的唯一原因可能有两个原因:(1) - 更轻松的深度访问(2) - 避免组件可能会弄乱不属于它的状态的错误
  • @vsync 您能否解释一下如何更轻松地进行深度访问?你的意思是现在可以使用本地 props 而不是必须引用全局状态,因此它更具可读性?
  • 另外,当状态作为不可变传入时,组件怎么会弄乱不属于它的状态?
  • 如果状态是不可变的,那么我想这很好,但作为一种好的做法,最好只向组件公开与它们相关的部分。这也有助于其他开发人员更好地了解(state 对象的)哪些部分与该组件相关。关于“更容易访问”,在某种意义上更容易将一些深度状态的路径作为道具直接传递给组件,并且该组件对幕后 Redux 的事实视而不见。组件不应该关心使用哪个状态管理系统,它们应该只使用它们收到的道具。
  • @vsync 我会添加 (3)。通过轻松查看哪些参数很重要,能够轻松跟踪和理解代码目的
【解决方案3】:

你答对了第一部分:

是的,mapStateToProps 将 Store 状态作为参数/参数(由 react-redux::connect 提供),它用于将组件与 store 状态的某些部分链接起来。

通过链接,我的意思是 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 的 react 组件处理显示并将过滤器状态持久化为 Redux Store 状态,Demo 组件正在“侦听”或“订阅” Redux Store 状态过滤器,因此每当过滤器存储状态更改时(在filtersComponent 的帮助下,react-redux 检测到发生了变化并通知或“发布”所有侦听/订阅的组件,方法是将更改发送到 componentWillReceiveProps,在本例中这将触发项目的重新过滤和由于反应状态已更改,因此刷新显示。

如果该示例令人困惑或不够清晰,无法提供更好的解释,请告诉我。

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

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

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

Redux Store 状态是 Redux reducer 状态的组合,每个状态负责管理一小部分应用逻辑。任何组件都可以在react-redux::connect@mapStateToProps 的帮助下访问这些减速器属性!这使得 Redux 存储状态可以在应用范围内访问,而组件状态是其自身独有的。

【讨论】:

    【解决方案4】:

    这个react & 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);
    

    此示例代码是一个很好的模板,可以作为您的组件的起点。

    【讨论】:

      【解决方案5】:

      React-Redux connect 用于为每个操作更新存储。

      import { connect } from 'react-redux';
      
      const AppContainer = connect(  
        mapStateToProps,
        mapDispatchToProps
      )(App);
      
      export default AppContainer;
      

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

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

      【讨论】:

      【解决方案6】:

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

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

      使用 mapStateToProps() 来改变状态的结构是不可能的。您正在做的只是选择组件需要的商店的键/值对并将值(从商店中的键/值列表)传递到组件中的道具(本地键)。您在列表中一次执行一个值。过程中不会发生结构变化。

      附:商店是当地的国家。 Reducers 通常也会将状态传递给数据库,Action Creators 也参与其中,但在这篇具体的帖子中首先要了解这个简单的概念。

      附言最好将 reducer 分成单独的文件,然后只导入组件需要的 reducer。

      【讨论】:

        【解决方案7】:

        这是描述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} />;
            }
          }
        }
        

        【讨论】:

          【解决方案8】:

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

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

          如果您想将与状态相关的逻辑从渲染函数转移到它之外,这将非常有用。

          【讨论】:

            【解决方案9】:

            我想重新构建您提到的陈述,即:

            这意味着目标组件所使用的状态可以 与状态完全不同的结构,因为它存储在 你的商店

            你可以说你的目标组件使用的状态有一小部分存储在 redux 存储中。换句话说,您的组件所使用的状态将是 redux 存储状态的子集。

            就理解 connect() 方法而言,它相当简单! connect() 方法有能力为你的组件添加新的道具,甚至覆盖现有的道具。正是通过这个 connect 方法,我们也可以访问由 Provider 提供给我们的 redux store 的状态。结合使用对您有利,您可以将 redux 存储的状态添加到组件的 props 中。

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

            【讨论】:

            • 我所说的不同结构的意思超出了子集问题,这也是正确的。例如,您可以在 store 中存储一个集合(即students:{...}),该对象是具有唯一键的对象,每个键对应于具有公共属性的对象(即{[uuid]:{first_name, last_name, age, major, minor}})。组件可以将其转换为这些属性之一的唯一值数组(即[...major])。不同的组件可能会以不同的方式对其进行转换(即 [...${first_name} ${last_name}])
            【解决方案10】:
            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);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-05-17
              • 2020-02-16
              • 1970-01-01
              • 2018-10-05
              • 1970-01-01
              • 2019-01-15
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多