【问题标题】:How to properly memoize mapDispatchToProps?如何正确记忆 mapDispatchToProps?
【发布时间】:2018-01-11 18:07:03
【问题描述】:

首先,这是我的 HOC:

export default function connectField({
         nameProp = 'name',
         valueProp = 'value',
         dispatchProp = 'dispatch'
     }: ConnectOptions) {

    return compose(
        getContext(contextTypes),
        connect((state, ownProps) => {
            const path = [namespace,...getPath(ownProps),...toPath(ownProps[nameProp])];
            const value = getOr('', path, state);
            return {
                [valueProp]: value
            };
        }, (dispatch,ownProps) => { // <----------- mapDispatchToProps
            const path = [...getPath(ownProps),...toPath(ownProps[nameProp])];

            return {
                [dispatchProp]: value => dispatch({type: ActionTypes.Change, payload: {path, value}})
            };

        }, (stateProps, dispatchProps, {[FIELD_PATH]: _, ...ownProps}) => {
            return {...stateProps, ...dispatchProps, ...ownProps}; 
        }, {
            areMergedPropsEqual: (a,b) => {
                let eq = shallowEqual(a,b);
                console.log('areMergedPropsEqual',a,b,eq);
                return eq;
            },
        }),
        withContext(contextTypes, props => {
            return {[FIELD_PATH]: [...getPath(props), props[nameProp]]};
        }),
    );
}

中间是我的mapDispatchToProps。这导致 areMergedPropsEqual 每次都返回 false,因为它每次都在创建一个新的动作创建者。

我不知道如何记住这一点:

value => dispatch({type: ActionTypes.Change, payload: {path, value}})

这样我每次都返回相同的函数实例。

in the docs 有一些关于“每实例记忆”的注释,这是我想要的,但我无法完全确定我应该在这里做什么。


说清楚,我知道如何记忆一个函数。但是,我不想使用具有无限历史的大缓存。这是不必要的内存消耗。我只需要像how reselect does it 这样的缓存大小为1。问题是我不能直接在connectField 中创建“选择器”,因为这仍然会创建一个共享实例——即,所有“连接字段”将共享相同的缓存,并且它们会相互覆盖,否定益处。它必须是每个组件实例这是特定于 React-Redux 的 connect 方法。它有一个语法,因此您可以在正确的位置创建选择器,并且每个实例只会运行一次。我只是在破译 API 时遇到了麻烦——他们是否期望一个函数返回一个返回对象的函数?还是一个以 propnames 作为键、函数作为值的对象?该函数返回什么?即,文档并不清楚 mapDispatchToProps 选项接受的所有不同变体。

【问题讨论】:

  • @KyleRichardson 仔细阅读mapDispatchToProps。它需要一个“对象函数”。然后,如果函数采用一两个参数,它会再次执行不同的操作。然后在“高级场景”下,它说函数可以返回一个函数。所以呃......我想我想给它传递一个带有一个参数的函数并返回一个返回一个对象的函数。没关系,等我弄明白为止。
  • 我很抱歉偏离了您所寻找的内容。我现在明白了这个问题,这是一个非常有趣的问题。还在考虑...如果您提出解决方案,请您更新,以便我可以查看想法:)

标签: reactjs redux react-redux memoization


【解决方案1】:

如果您已经拥有lodash,则您拥有memoize 函数,它允许将任何函数转换为记忆函数。这个记忆函数将计算给定参数的返回值,然后每次提供相同的参数时总是返回相同的返回值。

例如,您可以像这样使用它:

import { memoize } from 'lodash'

const makeChangeDispatcher = memoize( (dispatch, path) => 
     value => dispatch({type: ActionTypes.Change, payload: {path, value}})
)

...

    (dispatch,ownProps) => { // <----------- mapDispatchToProps
        const path = [...getPath(ownProps),...toPath(ownProps[nameProp])];

        return {
            [dispatchProp]: makeChangeDispatcher(dispatch, path)
        };

    }

...

您可以在lodash documentation of memoize查看更多信息

【讨论】:

  • 我认为你没有抓住重点。我遇到问题的部分是per-instance memoization。您已经创建了一个具有无限历史的全局记忆函数。它可能有效,但我的目标只是避免由于道具更改而重新渲染。为此,我只需要 1 的历史大小,但它必须是每个实例。对不起,我应该说得更清楚。
  • 是的,增长的历史可能是一个潜在的问题,但这会为您提供一个基于调度函数(始终相同)的记忆函数和取决于您的组件道具的路径。因此,您将拥有实例功能。由路径值精确地陷入泥潭。因此,具有相同路径的两个组件将共享相同的功能(这不是问题)。这里有什么问题?我错过了重点^^
  • 问题是缓存不需要随着道具的变化而增长。我要做的就是避免由于道具更改而重新渲染。如果道具没有改变,我想要返回相同的调度功能。如果 do 改变了,那么可能会返回一个新的调度函数。为此,我只需要 1 的缓存大小。
猜你喜欢
  • 2019-11-30
  • 2017-02-20
  • 2019-05-29
  • 2022-01-24
  • 1970-01-01
  • 2018-06-25
  • 2011-09-18
  • 1970-01-01
  • 2019-10-02
相关资源
最近更新 更多