【问题标题】:Redux - Understanding advanced mapStateToProps returning a functionRedux - 了解返回函数的高级 mapStateToProps
【发布时间】:2018-10-05 00:31:56
【问题描述】:

我试图了解 mapStateToProps 返回函数时的机制。

所以我找不到太多文档,除了 Redux 文档中的一小段摘录,它预先说明通过返回一个函数的情况,每个实例都将获得自己的 memoized mapStateToProps 并且另一个用户说这是一种防止 mapStateToProps 被要求任何父道具更改。

所以这对于列表项来说似乎很棒,我不想为不影响项目的任何更改重新渲染大型项目列表。

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

更新:

想澄清一下,我说的特别是mapStateProps的工厂函数版本。

这是我正在谈论的功能,取自 React-Redux 文档:

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

以及摘自这篇文章的段落:

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

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

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

【问题讨论】:

    标签: javascript reactjs redux react-redux


    【解决方案1】:

    如果状态发生变化,每个mapStateToProps 函数都会被调用。 redux 中没有包含阻止mapStateToProps 被调用的机制。

    From the docs for connect()

    mapStateToProps(state, [ownProps]): stateProps] (Function):如果指定了这个参数,新组件将订阅 Redux 存储更新。这意味着无论何时更新商店,都会调用 mapStateToProps

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

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

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

    考虑这个使用选择器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 中的选择器将连接组件的自己的 props 作为参数时,您需要使用 mapStateToProps 作为工厂以允许每个实例记忆。

    您可以在 redux 文档中的 Comuting Derived Data 下找到更详细的示例说明。

    【讨论】:

    • “选择器记住上次调用的结果” - 如果你使用像 reselect 这样的库。
    • @TrueWill 您可以轻松编写一个普通的 javascript 函数来执行此操作并将其称为选择器。但你是对的,reselect 可以帮助你创建这些选择器。
    • @iQ。 mapStateToProps 的调用从不被跳过。我一开始就这么写的。将始终为每个连接的组件调用它。也不例外。至少没有内置在 redux 中。
    • 好的,我已经进行了更多调查,也许已经到了某个地方,更新了我的问题。当您将 ownProps 包装到一个闭包中并返回一个仅使用状态的函数时,这似乎是一种特殊情况,因此它可以防止对每个连接的孩子调用父道具更改,至少我认为它是这样说的。
    • @iQ。是的,我也做了更多的研究,我想我现在知道你的意思了。你是对的。如果您使用闭包,mapStateToProps 将不会被来自父级的 prop 更改调用。但它仍然会在每次状态更改时调用。我将编辑我的答案以添加该特殊情况以及演示这一点的代码框示例。
    【解决方案2】:

    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 }就可以决定是否显示列表中选中项的详细信息。

    【讨论】:

      猜你喜欢
      • 2016-11-07
      • 2019-06-06
      • 2017-08-18
      • 2021-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-28
      • 2019-04-01
      相关资源
      最近更新 更多