当redux状态表明它不应该被包裹时,包裹的组件仍会呈现

时间:2016-07-07 12:59:57

标签: reactjs redux react-redux

我有这个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
}

组件的道具来自连接的redux容器组件

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

我的account缩减器看起来像这样:

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,因此有效。

我想知道那可能是什么样的种族情况?

1 个答案:

答案 0 :(得分:1)

我不知道如果没有看到代码,但看起来你的reducer会改变状态。减速器不应该改变状态。它应该改为创建一个具有正确属性的新状态。

这一点的全部意义在于防止竞争条件和其他异常现象。

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

如果您的reducer修改旧状态并返回它,则Redux没有简单的方法可以看到您对状态进行了修改。事实上,它将假设没有做出任何改变。因此,没有任何东西会被重新渲染。

修改

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

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

当您的州发生变化时,connect会通过调用props重新评估WrappedComponent mapStateToProps,并且会发现这些道具已经更改(由于不再登录,因此这个数据与以前不同)。然后,Connect将指示React重新呈现WrappedComponent。 React会这样做。反过来,您的render方法可能会对传递的数据造成问题,因为它的无效数据仅在用户登录时使用。

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

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

最终订单取决于渲染顺序和安装顺序,许多组件在决定此顺序时起作用。它根本不稳定,因此不应该依赖它。

构建连接组件时,请务必编写mapStateToProps以获取任何有效状态,并从目标组件的那些编译有效道具。无论是添加虚拟值,还是更改组件本身以使传递给它的任何内容都有效,重要的是允许所有有效状态解析为有效道具,即使这些道具所用的组件从未打算显示。通过这种方式,您可以防止由于无效道具导致的错误,这些道具实际上是由有效状态导致的(被注销的是有效状态)。

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