Redux: 组件未使用connect()重新渲染

创建于 2015-08-20  ·  32评论  ·  资料来源: reduxjs/redux

我已经通过阅读文档来了解Redux,并且开始只是修改实际示例。 就我而言,我已经将GitHub HTTP API调用替换为自己的调用,并将Repo和User概念与环境中的类似概念交换了。 到目前为止,应该不会有太大不同,对吗?

这是我遇到麻烦的地方:

我的API调用成功,响应JSON变得驼峰化并被规范化,但在API请求成功发生后_but_我的组件未重新呈现。 在调度USER_REQUEST并且用户组件更新为显示“正在加载...”后不久,调度了USER_SUCCESS ,其下一个状态确实包含一个填充的entities键:

pasted_image_8_19_15__3_27_pm

那将意味着问题出在中间件上,对吗? 我应该将动作中的实体存储在商店中,并且组件应该观察到更改并重新呈现,是吗?

在实际示例中,成功地将已检索和标准化的数据“实际”放到了哪里? 我看到正在规范化的位置,但是看不到将规范化的结果传递到商店的位置。

非常感谢!

question

最有用的评论

数据通过reducer中的处理操作结果在存储中进行设置/更新/删除。 Reducer会收到您应用程序切片的当前状态,并希望重新获得新状态。 可能不重新渲染组件的最常见原因之一是,您正在修改化简器中的现有状态,而不是返回具有必要更改的新状态副本(请参阅“疑难解答”部分)。 当您直接更改现有状态时,Redux不会检测到状态差异,也不会通知组件存储已更改。

因此,我绝对会检查您的化径器,并确保您不会改变现有状态。 希望有帮助!

所有32条评论

数据通过reducer中的处理操作结果在存储中进行设置/更新/删除。 Reducer会收到您应用程序切片的当前状态,并希望重新获得新状态。 可能不重新渲染组件的最常见原因之一是,您正在修改化简器中的现有状态,而不是返回具有必要更改的新状态副本(请参阅“疑难解答”部分)。 当您直接更改现有状态时,Redux不会检测到状态差异,也不会通知组件存储已更改。

因此,我绝对会检查您的化径器,并确保您不会改变现有状态。 希望有帮助!

除了@ernieturner所说的以外,请确保connect(mapStateToProps)(YourConnectedComponent)函数中的mapStateToProps函数映射相关状态(在本例中entities ),以便连接的组件正在侦听到那片状态。

此外,请记住connect()做一个浅表比较: https :

可能是因为您的连接状态的形状没有改变,所以假设不进行更新。

啊,明白了! 我的mapStateToProps试图从状态中提取对象,但未指定正确的键。 我没有提取entities.users[login] ,而是将其设置为entities.users[id]entities.users对象由login字符串作为键,因此,当找不到数字id时,实际上没有任何道具更改,因此不需要重新渲染。

谢谢大家的帮助! 非常感谢您花费的时间为您提供帮助。

@timdorr
这是荒谬的。 如何强制connect()检查深层更改? 否则,必须为每个更深层次的状态创建数十个容器组件。 不正常

您绝对应该在任何大小适当的应用程序中具有许多连接的组件。 如果您只有一个顶级连接组件,那么每次状态更改时,都将不必要地重新渲染应用程序的大部分。 而且您将避免react-redux的主要优化之一,该优化仅在订阅状态改变时才重新渲染。

如果您需要评估状态的复杂查询,请使用reselect加快速度。

@timdorr @wzup

阐明严格解决深度比较问题并不一定需要在树的较低位置进行连接可能会有所帮助。

在组件树的较低位置进行连接确实可以帮助_performance_,但这并不是深度比较问题的根本解决方案。 深度比较问题的解决方案是让还原器不可变地更新状态,以便进行较浅的比较足以了解较深的状态何时更改。

换句话说,只要简化器返回新状态而不更改现有状态对象,即使将顶级组件连接到一个大型嵌套状态块(即使使用浅比较)也可以正常工作。

@naw感谢您的提示。 基于此,我最终在遇到此问题的地方添加了一个新的减速器:

const reducers = combineReducers({
    //...bunch of reducers which always return objects of 3 or 4 properties that change sometimes but
    // the shape stays the same
    dataVersion: (state = Symbol(), action = {}) => {
        switch (action.type) {
            case SAME_ACTION_AS_THE_ONE_THAT_UPDATES_A_COMPLEX_PROPERTY:
            case ANOTHER_ACTION_THAT_ALSO_UPDATES_A_COMPLEX_PROPERTY:
                return Symbol():
            default:
                return state;
        }
    }
})

不过,这仍然让我感到很奇怪。 如果我有一个减速器:

    switch (action.type) {
       case UPDATE_THIS:
           return {a: action.a, b: action.b, c: action.c};
       default:
           return state;
     }

我返回一个刚好与前一个状态具有相同形状的新初始化对象这一事实应该足以触发previousState !== newState 。 如果我应该每次都返回不可变的数据,那么进行形状的浅表比较而不是仅仅进行身份标识似乎效率较低。 (假设这就是Redux实际在做的,就是这样。事实上,我的Symbol解决方法起作用了,这让我觉得这就是事实。)

在这种特殊情况下,添加更多的容器组件无法解决问题。 可悲的是,我无法链接到整个github项目进行澄清,因为这是我在这里处理的公司的封闭源代码。

@Zacqary当前状态与先前状态的比较是通过严格等于(===)完成的。 stateProps(mapStateToProps的结果)的比较是使用浅相等进行的。

扎卡里

mapStateToProps通过进入商店来“连接”所连接组件的数据。

mapStateToProps将各种片段从商店中拉出,并将它们分配给新的stateProps对象中的键。

生成的stateProps对象每次都会是新的(因此其标识也会更改),因此我们不能只比较stateProps对象本身的对象标识---我们不得不放弃一个对象级别并检查每个值的对象标识,这就是shallowEqual起作用的地方,如@jimbolla所述。

通常,当您以这样的方式编写reducer时会出现问题:状态状态的对象标识_doesn't change_,而其中的某些嵌套属性_does_则更改。 这是在改变状态,而不是一成不变地返回新状态,这导致react-redux认为没有任何更改,而实际上是某些更改。

我要返回刚好与先前状态具有相同形状的新初始化对象这一事实应该足以触发previousState!== newState。

如果为某个状态片返回一个新对象,并且mapStateToProps使用该状态片作为其键之一的值,则react-redux将看到对象标识已更改,并且将不能阻止重新渲染。

是否有某些特定功能无法正常运行? 我不确定您的Symbol解决方法是什么意思。

值得考虑的是,是否应将附加组件简单地移至connected()组件中以继承道具。 通过尝试connect()两个组件来解决这个问题,但这是不必要的复杂性。

@ernieturner我知道这很旧,但是您可能要更新“故障排除部分”链接。

干杯

我有这个问题,很不完全是。
我将属性应用于状态,例如:

  constructor(props){
    this.state = {
      text: props.text
    }
  }

而且没有用。 所以我直接从诸如

{this.props.text}

它工作得很好:)

@AlexanderKozhevin除非我弄错了,否则您的构造函数将缺少super(props)
通常,在致电super(props)之前,甚至不允许使用this super(props)

@ cdubois-mh对,谢谢,谢谢。

我已经编写了定制的应用程序根减速器,然后遇到了这个问题。
返回整个状态的副本
{...state}
在根减少剂的最后对我有帮助

@daedalius减速器应该是这样工作的。
如果操作无关紧要,则应返回状态,或者返回具有预期修改值的状态副本

如果您不返回副本,那么react并不总是能够检测到值已更改。
(例如,如果您修改嵌套条目)

检查此状态:

{
    "clients": [
        { "name": "John", "cid": 4578 },
        { "name": "Alex", "cid": 5492 },
        { "name": "Bob",  "cid": 254 }
    ]
}

如果您修改客户端的名称,但不返回状态的克隆,则您没有修改状态,而是修改了数组中的对象。

对象本身仍将具有与以前相同的引用,因此react将丢失更改。
通过返回一个克隆,状态本身的引用现在将有所不同(因为它是一个新对象),而react将启动整个重新渲染过程。

如果我错了,请纠正我,但这就是我理解减速器工作的方式。

是的,没错。 另外, @daedalius ,请注意,您不希望总是复制state的浅表副本-仅应在发生某些更改的情况下进行此复制,否则可能会不必要地重新渲染组件。

强制性升级

<AfterConnect
    _forceUpdate={Symbol()}
/>

@BrookShuihuaLee那是什么意思? 您没有提供任何有关代码段的信息或说明。 它有什么作用? 为什么它与当前的讨论有关?

@CedSharp如果设置_forceUpdate = {Symbol()},则shallowEqual函数将返回false。

@BrookShuihuaLee如果您可以在第一个答案中包含这样的解释,那就太好了,这样人们会更好地理解它^^

@CedSharp是的

@BrookShuihuaLee :这似乎是一个坏主意。 你为什么想这么做?

@BrookShuihuaLee ,我同意@markerikson的观点
原因是当状态未更改时,您不应该返回克隆。 除非您提供一个有效的示例来说明您的解决方案是必要的,否则我强烈建议您不要讨论Redux的工作方式。

@CedSharp
@markerikson
因为组件AfterConnect将根据设计进行频率重新渲染。 可以在重新渲染其父组件时再次重新渲染它。 我不想在父组件中进行所有计算并为AfterConnect设置大量道具。

@BrookShuihuaLee ,很抱歉,但是您的内容与当前的讨论有何关系?
如果我理解正确,那么您是在谈论自己的自定义组件吗? 在React世界中,强烈建议不要使用ForceUpdate,我不建议将其作为解决方案。

相反,您可以使用一种可观察的解决方案,在其中寻找状态以外的其他地方的变化? 我不知道为什么您需要在不修改其状态的情况下重新渲染该特定组件,但是如果您知道为什么应该重新渲染该组件,也许可以使用诸如mobx之类的东西?
(https://github.com/mobxjs/mobx)

我试图了解您的解决方案是什么,以及为什么在这种情况下适用。

@CedSharp我并不是说每个人都应该使用forceUpdate。 但是推荐是一回事,选择是另一回事。 React还为开发人员保留了ReactComponent.prototype.forceUpdate。 人们需要选择。

我试图给另一个选择。 也许,这不是最佳选择。

并非最佳,但由于我的应用程序状态结构不正确,我遇到了这个问题。 我只是通过在引起我麻烦的状态的一部分上更新浅表字段来解决浅表比较问题。

case SOME_ACTION:
      return {
        ...state,
        ts: (new Date).getTime(),
      };

IMO不是一个好的解决方案,但是对于当前处于绑定状态的任何人(例如,您必须释放一天结束),这是一个快速解决方案,您不必强制在其他地方进行渲染。

应该对代码进行重构,以便即使在深度嵌套的状态下也可以进行较浅的比较。

状态必须是不变的。 在reducer中深度复制状态,然后在连接中进行浅表比较就足够有效了。

我的问题是在其中有一个数组对象。 由于比较浅,所以它从未到达子数组中的对象。 我通过在mapStateToProps中使用list:state.obj.list而不是lists:state.obj来解决了此问题

我刚遇到此问题,在阅读了第一批答复后,我检查了减速机,发现那里有错字。 我重命名了一个属性并更新了INITIAL_STATE,但是我忘记更改此有问题的操作正在更新的密钥。

我基本上是在更新组件中未使用的某个随机密钥,因此很明显,在更新此随机密钥时,它不会重新呈现。

检查减速器中的打字错误! :)

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

mickeyreiss-visor picture mickeyreiss-visor  ·  3评论

rui-ktei picture rui-ktei  ·  3评论

ms88privat picture ms88privat  ·  3评论

vraa picture vraa  ·  3评论

jimbolla picture jimbolla  ·  3评论