【问题标题】:How does Redux Reselect memoization work?Redux Reselect memoization 是如何工作的?
【发布时间】:2018-10-02 11:35:36
【问题描述】:

我正在尝试将reselect 集成到我当前的应用程序中,并且一如既往,首先我开始阅读文档,然后如果需要,另一个资源。我无法理解文档的一个特殊部分,也找不到以更清晰的方式解释的资源。现在我来这里是为了得到一些清晰的解释。 所以它在文档中说`

import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
  <div>
    <VisibleTodoList listId="1" />
    <VisibleTodoList listId="2" />
    <VisibleTodoList listId="3" />
  </div>
)

将 getVisibleTodos 选择器与多个实例一起使用 VisibleTodoList 容器将无法正确记忆:

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { getVisibleTodos } from '../selectors'

const mapStateToProps = (state, props) => {
  return {
    // WARNING: THE FOLLOWING SELECTOR DOES NOT CORRECTLY MEMOIZE
    todos: getVisibleTodos(state, props)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

使用 createSelector 创建的选择器的缓存大小为 1,并且只有 当其参数集与其相同时返回缓存值 上一组参数。如果我们在渲染之间交替 &lt;VisibleTodoList listId="1" /&gt; 和 &lt;VisibleTodoList listId="2" /&gt;, 共享选择器将在接收{listId: 1} 和 {listId: 2} 作为它的 props 参数。这将导致论点 每次调用都不同,因此选择器将始终重新计算 返回缓存的值。

注意最后一句话。如果我们传递不同的ids,为什么要返回缓存值,它应该返回不同的值取决于ids?

【问题讨论】:

    标签: reactjs redux react-redux reselect


    【解决方案1】:

    所以我们让这个选择器为我们的VisibleTodoList 组件获取状态:

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

    如果我们渲染组件:

    return (
        <div>
            <VisibleTodoList listId="1" />
        </div>
    )
    

    然后,选择器被调用为:getVisibleTodos(state, { listId: 1 }),选择器将结果(待办事项列表 1 对象)存储(记忆)在内存中。

    如果我们使用相同的 props 渲染组件两次:

    return (
        <div>
            <VisibleTodoList listId="1" />
            <VisibleTodoList listId="1" />
        </div>
    )
    
    1. 选择器被调用,结果被记忆

    2. 选择器被第二次调用,它看到 { listId: 1 } 与第一次是相同的 prop 参数,所以它只返回记忆值。

    如果我们用不同的 props 渲染组件两次:

    return (
        <div>
            <VisibleTodoList listId="1" />
            <VisibleTodoList listId="2" />
        </div>
    )
    
    1. 选择器被调用,结果被记忆

    2. 选择器被第二次调用,它发现 { listId: 2 } 与第一次 { listId: 1 } 的 props args 不同,因此它重新计算并记住新结果(待办事项列表 2 对象)记忆(覆盖之前的记忆)。

    如果我们希望每个组件都有自己的记忆,每个组件实例必须有自己的选择器实例。

    例如:

    // selector
    const makeGetVisibleTodos = () => createSelector(
        // ... get the visible todos
    );
    
    // each has their own memoization space:
    const foo = makeGetVisibleTodos(); // this is an instance
    const bar = makeGetVisibleTodos(); // this is a separate instance
    

    所以将它应用到组件上:

    // component
    const mapStateToProps = () => {
        const getVisibleTodos = makeGetVisibleTodos(); // this instance get bound to the component instance
    
        return (state, props) => {
            return {
                todos: getVisibleTodos(state, props)
            }   
        }
    }
    

    现在,如果我们用不同的 props 渲染组件两次:

    return (
        <div>
            <VisibleTodoList listId="1" />
            <VisibleTodoList listId="2" />
        </div>
    )
    
    1. 使用&lt;VisibleTodoList listId="1" /&gt; 调用选择器的第一个实例并记录结果

    2. 使用 &lt;VisibleTodoList listId="2" /&gt; 调用选择器的不同实例并记录结果

    【讨论】:

    • 这是有道理的......感谢您的深入解释
    • 检查每个道具以查看是否有任何变化,而不是再次进行计算,这不是很昂贵吗?如果它是非昂贵的物品怎么办? “检查”不是看它是否记忆了一项昂贵的操作吗?
    【解决方案2】:

    不,它不会返回错误的值。文档只是说对于这种情况,记忆化根本不起作用。为了使它起作用(意思是“节省一些资源并避免重复相同的计算”),您需要。

    实际上文档说(您引用的部分中的最后一句话):

    我们将在下一节看到如何克服这个限制。

    next section Sharing Selectors with Props Across Multiple Component Instances 说

    要在多个 VisibleTodoList 实例之间共享选择器,同时传递 props 并保留记忆,组件的每个实例都需要自己的选择器私有副本。

    当然,您也可以将记忆大小增加到 1 以上。

    【讨论】:

    • 我已经阅读了整个文档,我知道有一种方法可以解决这个问题,但我的问题不是那个。他们说`If we alternate between rendering &lt;VisibleTodoList listId="1" /&gt; and &lt;VisibleTodoList listId="2" /&gt;, the shared selector will alternate between receiving {listId: 1} and {listId: 2} as its props argument. This will cause the arguments to be different on each call, so the selector will always recompute instead of returning the cached value.请注意最后一句话。为什么id-s不同时选择器需要返回缓存值?
    • 它不返回缓存值。它重新计算。因为id不同。
    • 没错,这就是我误解的重点。是的,它会重新计算,因为 IDs 基于 IDs 从存储中检索不同的数据是不同的。那么为什么他们把它当作一个问题呢? 但是有一个问题! Using the getVisibleTodos selector with multiple instances of the VisibleTodoList container will not correctly memoize: 那他们为什么说这是个问题呢?
    • 记忆应该有助于通过避免重复复杂/繁重的计算来节省资源。在这种情况下,你需要做额外的动作,否则记忆将毫无用处
    • 对不起,你无法抓住我的心。我知道记忆的目的。在这种情况下,他们通过不同的IDs 来检索不同的数据,所以选择器会重新计算是正常的,但他们说这是一个问题,它应该返回缓存值
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-19
    • 2016-10-11
    • 2019-01-20
    相关资源
    最近更新 更多