1

基于我刚刚阅读并重新阅读的所有 Redux 和 Reselect 文档,thing.toJS()如果getThing()返回的不可变映射不等于前一个,则下面的选择器应该只进行处理。

...

// Selector

import { createSelector } from 'reselect'

const getThing = (state, thingId) => {
    return state.entities.getIn(['things', thingId])
}

const makeThingSelector = () => {
    return createSelector(
        [getThing],
        (thing) => {
            return thing.toJS()
        }
    )
}

export default makeThingSelector

...

// Container

const makeMapStateToProps = () => {
    return (state, ownProps) => {
        const { thingId } = ownProps
        const things = select.makeThingsSelector()(state, thingId)

        return {
            hasNoThings: things.length === 0,
            things
        }
    }
}

const Container = connect(
    makeMapStateToProps,
    mapDispatchToProps
)(Component)
...

除非我有一个孩子的“智能”组件,否则这是正确的。在这种情况下,当父组件触发渲染时,子组件容器中调用的选择器总是处理该值,而不管结果是否是新的。

我一直在尝试将 ImmutableJS API 封装在我的选择器中,但这意味着要避免每次它们的父组件更新时重新渲染这些嵌套组件,我必须在shouldComponentUpdate函数中进行深度相等检查。这很昂贵,而且似乎不是一个体面的解决方案。

应用程序状态被规范化,因此状态树的更新部分不是子组件所依赖的状态部分的分层父级。

我在这里错过了一些关键吗?

4

2 回答 2

4

在每次store更新react-redux时执行以下步骤(将所有内部复杂性放在一边):

  1. 呼叫mapStateToPropsmapDispatchToProps
  2. 浅比较的结果props
  3. 重新渲染Component以防万一新props的与以前的不同。

每次更新设计mapStateToProps都会调用这种方式。store以下代码行也是如此:

...
const things = select.makeThingsSelector()(state, visitId)
...

正如您所看到的reselect,每次都会创建新的选择器,从而有效地防止任何记忆(在 中没有全局状态reselect,每个选择器都会发生记忆)。

您需要做的是更改您的代码,以便在每次调用时都使用一个相同的选择器mapStateToProps

const thingSelector = select.makeThingsSelector();

...

const makeMapStateToProps = () => {
    return (state, ownProps) => {
        const { visitId } = ownProps
        const things = thingSelector(state, visitId)

        return {
            hasNoThings: things.length === 0,
            things
        }
    }
}

更新:我也看不出有任何理由使用工厂风格makeThingsSelectormakeMapStateToProps. 为什么不直接使用类似的东西:

...

// Selector

export default createSelector(
  [getThing],
  (thing) => thing.toJS()
);

...

// Container

const mapStateToProps = (state, ownProps) => {
  const { visitId } = ownProps
  const things = select.thingsSelector(state, visitId)
  return {
    hasNoThings: things.length === 0,
    things
  }
}

const Container = connect(
    mapStateToProps,
    mapDispatchToProps
)(Component)
...
于 2017-03-30T07:04:10.193 回答
2

由于此应用程序中的 redux 状态使用ImmutableJS数据结构,Reselect因此可能不是必需的。

首先,ImmutableJS仅操作受更改操作影响的数据结构切片,因此对较大状态的所有更改可能不会影响传递给容器的切片。

其次,redux connect 函数默认返回一个纯容器,遇到相同的切片不会重新渲染。但是,mapStateToProps由于整个状态和可能的 ownProps 都已更改,因此将调用 。

为了更好地控制,同一容器的渲染可以直接链接到对特定状态切片的更改,并通过向连接函数的第四个参数(更好地称为对象)ownProps添加areStatesEqual和谓词属性。areOwnPropsEqualoptions

const mapStateToProps = ({ entities }, { thingId }) => {
    const things = entities.getIn(['things', thingId]).toJS();
    return {
        hasNoThings: things.length === 0,
        things
   };
};

const Container = connect(
    mapStateToProps,
    mapDispatchToProps,
    undefined, {
        areOwnPropsEqual: (np, pp) => np.thingId === pp.thingId,
        areStatesEqual: (ns, ps) => ns.entities.get(‘things’).equals(
            ps.entities.get(‘things’)
        )
    }
)(Component);

如果这两个谓词都为真,则不仅容器及其子容器不会重新渲染,mapStateToProps甚至不会被调用!

于 2019-07-08T10:49:08.840 回答