首页 文章

Redux - 了解返回函数的高级mapStateToProps

提问于
浏览
4

我试图理解mapStateToProps返回函数时的机制 .

所以我找不到很多文档,除了Redux文档的一个简短的摘录,通过返回一个函数提前说明,每个实例将获得自己的memoized mapStateToProps,另一个用户说这是一个优化,阻止mapStateToProps被调用父母道具变化 .

所以对于列表项来说这看起来很棒,我不想为任何不影响项目的更改重新呈现大项目列表 .

因此,让我感到困惑的部分是,不会为任何父道具更改调用mapStateToProps,这是否意味着为了重新呈现单个列表'Item',它需要是一个智能连接组件才能获取它改变它关心并重新渲染?或者这是否意味着它永远不会为这个特定的Item实例重新渲染?

Update:

想澄清我正在谈论mapStateProps的工厂功能版本 .

以下是我所讨论的功能,取自React-Redux文档:

注意:在需要更多控制渲染性能的高级场景中,mapStateToProps()也可以返回一个函数 . 在这种情况下,该函数将用作特定组件实例的mapStateToProps() . 这允许您进行每个实例的memoization . 您可以参考#279及其添加的测试以获取更多详细信息 . 大多数应用从不需要这个 .

从本文中取出的段落:

https://medium.com/@cvetanov/redux-mapstatetoprops-optimization-5880078a8a7a

如果redux接收到一个返回函数的实现,它会执行一个闭包来包装组件自己的props,因此每次组件更改它从父组件接收的props时,都会绕过mapStateToProps的调用 . 它创建了一个所谓的purePropsSelector . 如何做到这一点可以在这里看到 .

Update 2:

我正在调查提到跳过的文章,它似乎是当你在一个闭包中包装自己的道具并返回一个只使用状态的函数 . 因此,当父内容为每个“已连接”子项更改时,它会阻止调用mapStateToProps .

这取自我上面读到的那篇中篇文章:

function mapStateToPropsFactory(initialState, ownProps) {
  // a closure for ownProps is created
  // this factory is not invoked everytime the component
  // changes it's props
  return function mapStateToProps(state) {
    return {
      blogs:
        state.blogs.filter(blog => blog.author === ownProps.user)
    };
  };
}
export default connect(mapStateToPropsFactory)(MyBlogs);

2 回答

  • 5

    ownProps 的第二个参数是传递给要包装的组件的道具 . 你提到了列表项,所以我们称之为 (ListItem) .

    所以 ListItem 组件,当它显示在屏幕上时,会传递一些列表库,例如,

    const { id, title } = this.props.library;

    ownProps 对象完全等于组件内的 this.props .

    因此,您传递到组件中的任何项目都将在 mapStateToProps 中显示为 ownProps . 通过访问 mapStateToProps 内的 ownProps

    const mapStateToProps = (state, ownProps) => {
      return { selectedLibraryId: state.selectedLibraryId };
    };
    

    您可以准确预先确定要在组件内部传递的道具,这样您就可以完全删除组件中的所有逻辑:

    const mapStateToProps = (state, ownProps) => {
      const selected = state.selectedLibraryId === ownProps.library.id;
      return { selected };
    };
    

    组件不再需要担心 selectedLibraryId 的含义,它只需查看 { selected } 并决定是否从列表中显示所选项目的详细信息 .

  • 2

    如果状态改变,将始终调用每个 mapStateToProps 函数 . redux中没有包含阻止 mapStateToProps 被调用的机制 .

    来自connect()的文档:mapStateToProps(state,[ownProps]):stateProps](Function):如果指定了此参数,则新组件将订阅Redux存储更新 . 这意味着每次更新商店时,都会调用mapStateToProps .

    您想要阻止的(通常使用选择器)是在 mapStateToProps 内发生的昂贵计算将在每个状态更新时重复,即使它们产生相同的结果 .

    或者这是否意味着它永远不会为这个特定的Item实例重新渲染?

    如果连接组件收到的任何道具发生变化,它将照常重新渲染 . 关键是要防止 mapStateToProps 进行昂贵的计算 . mapStateToProps 很蠢 . 它进行计算并将它们作为道具传递给连接的组件 . 然后该组件检查道具是否与之前的道具不同,并决定基于此重新渲染 .

    考虑一下使用选择器 getVisibleTodosmapStateToProps 函数:

    const mapStateToProps = state => {
      return {
        todos: getVisibleTodos(state.todos, state.visibilityFilter)
      }
    }
    

    选择器记住调用的结果,只要输入参数没有改变,它就会在后续调用中返回该结果 . 此示例中的选择器仅从redux状态获取其输入 . 只要 state.todosstate.visibilityFilter 不改变它就可以使用上次调用的记忆结果而不需要重新计算某些东西 .

    现在考虑另一个例子:

    const TodoList = ({id, todos}) => (
      <ul id={id}>
        {todos.map(/* ... */)}
      </ul>
    );
    
    const mapStateToProps = (state, props) => {
      return {
        todos: getVisibleTodos(state, props)
      }
    }
    
    export default connect(mapStateToProps)(TodoList);
    

    这次选择器还将组件自己的道具作为输入 . 这是有问题的,因为如果我们使用连接的 TodoList 的两个实例并渲染它

    <TodoList id="list1" />
    <TodoList id="list2" />
    

    这将导致 mapStateToProps 在状态更新时被调用两次,每个 TodoList 实例一次 . 两者都有它会收到不同的道具 . 一次使用 {id: 'list1'} ,第二次使用 {id: 'list2'} . 但两个组件共享相同的选择器 . 即使每个 TodoListtodos 没有改变,这也会导致选择器重新计算 . 现在返回函数的 mapStateToProps 函数起作用:

    const makeMapStateToProps = () => {
      const getVisibleTodos = makeGetVisibleTodos() // this creates a new selector function
      const mapStateToProps = (state, props) => {
        return {
          todos: getVisibleTodos(state, props)
        }
      }
      return mapStateToProps
    }
    

    这为 TodoList 的每个实例创建了一个单独的 mapStateToProps 函数,它具有自己的选择器,以便它为每个实例单独存储道具,并且仅在为更改创建实例的道具时重新计算 . 这将解决前一个示例中的问题 .

    所以tl; dr;:当 mapStateToProps 中的选择器将连接组件的自己的道具作为参数时,你需要使用 mapStateToProps 作为工厂来允许每个实例的记忆 .

    您可以在redux文档中的Comuting Derived Data下找到更详细的解释 .

相关问题