【问题标题】:Wrapped component still renders when redux state says it shouldn't当 redux 状态说它不应该时,包装的组件仍然呈现
【发布时间】:2016-11-09 19:30:19
【问题描述】:

我有这个Protected HOC。其目的是仅在用户通过身份验证时呈现其WrappedComponent。否则应该呈现AuthenticateComponent(通常是登录组件)。

import React from "react"

const PROPTYPES = {
  authenticated: React.PropTypes.bool.isRequired,
}
export default (WrappedComponent, AuthenticateComponent) => {
  let Protected = (props) => (
    props.authenticated
    ? <WrappedComponent {...props}/>
    : <AuthenticateComponent {...props}/>
  )
  Protected.propTypes = PROPTYPES
  return Protected
}

组件的 props 来自一个连接的 redux Container 组件

const AccountContainer = ({ children }) => (
  <div>{children}</div>
)
const select = state => state.account
export default connect(select, { refreshUser, logout })(Protected(AccountContainer, LoginContainer))

我的account reducer 看起来像这样:

function authenticated(state = false, action) {
  switch (action.type) {
    case actions.START_SIGNUP_SUCCESS:
    case actions.LOGIN_SUCCESS:
      return true
    case actions.LOGIN_ERROR:
    case actions.START_SIGNUP_ERROR:
    case actions.LOGOUT_SUCCESS:
      return false
    default:
      return state
  }
}

...

export default combineReducers({
  authenticated,
  access_token,
  loggingIn,
  user,
  error,
})

现在发生的情况是,当设置LOGOUT 操作时,state.account.authenticated 属性设置为 false,但仍然呈现 WrappedComponent。它访问account 的各种其他属性,并且它们也都已被清除,组件不会检查和期望这些属性。 WrappedComponent 假定当它被渲染时,account 状态仍然是 authenticated 并且因此有效。

我想知道那可能是一种什么样的比赛条件?

【问题讨论】:

    标签: reactjs redux react-redux


    【解决方案1】:

    没有看到代码我无法知道,但看起来你的减速器改变了状态。 reducer 永远不应该改变状态。相反,它应该创建一个具有正确属性的新状态。

    这样做的重点是防止竞争条件和其他异常情况。

    React Redux 经过精心设计,可以防止您在此处看到的各种问题。它确实要求您遵守 Redux 规则。主要的一点是reducers 必须是纯函数。

    如果你的 reducer 修改了旧的 state 并返回它,Redux 没有简单的方法可以看到你对 state 做了修改。事实上,它将假定没有进行任何更改。因此,不会重新渲染任何内容。

    编辑:

    你的减速器看起来不错,尽管我看不到 account 减速器在其他地方是如何使用的,但我认为它也很好。

    我认为问题在于您的组件实际上并没有被渲染,但渲染方法仍然在LOGOUT 上被调用。发生的事情是,如果子组件请求这样的渲染,React 会很高兴地渲染该子组件。并且由于 connect 挂钩到 store 更新以实现这一点,这就是正在发生的事情。

    当你的状态发生变化时,connect 会通过调用mapStateToProps 重新评估WrappedComponentprops,它会发现那些props 发生了变化(由于不再登录,因此这些数据与以前不同)。然后 Connect 将指示 React 重新渲染 WrappedComponent。 React 会这样做。反过来,您的 render 方法可能在传递的数据方面存在问题,因为它是无效数据,只能在用户登录时使用。

    解决方案是简单地使用虚拟&lt;div/&gt; 退出渲染。这个 div,一个虚拟 DOM 元素,实际上永远不会进入 DOM。 React 缓存元素并批量更新 DOM。因此,React 会在将整个 WrappedComponent 合并到 DOM 之前修剪整个 WrappedComponent,不幸的是,在它已经呈现新版本之后。

    请注意,真正的罪魁祸首是 Redux 与 Connect 的结合,因为 Redux 没有组件的概念,因此无法将它们考虑在内,而 Connect 按照创建顺序订阅 store,并且然后 store 按照订阅的顺序通知组件。

    最终顺序取决于渲染顺序和安装顺序,许多组件在决定此顺序时发挥作用。它根本不稳定,因此不应指望。

    当您构建连接组件时,请确保编写您的 mapStateToProps 以获取 任何 有效状态并从目标组件的那些中编译 valid 道具。无论您添加虚拟值,还是更改组件本身以使您传递给它的任何内容都有效,重要的是允许所有有效状态解析为有效道具,即使这些道具用于的组件永远不会被显示。这样,您就可以防止由于无效的 props 而导致的错误,而这些 props 实际上是由有效状态引起的(退出是一种有效的状态)。

    当然,没有必要处理无效状态,因为它不应该发生,即使是瞬间也不应该发生。

    【讨论】:

    • 我已经用我的 reducer 代码编辑了我的问题。你觉得还可以吗?
    猜你喜欢
    • 1970-01-01
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 2017-10-27
    • 2022-06-15
    • 2016-12-05
    • 1970-01-01
    • 2017-08-24
    相关资源
    最近更新 更多