警告:setState(...):在使用redux/immutable的现有状态转换期间无法更新

Jie*_*eng 10 javascript reactjs redux react-redux

我收到了错误

警告:setState(...):无法在现有状态转换期间更新(例如在内部render或另一个组件的构造函数中).渲染方法应该是道具和状态的纯函数; 构造函数的副作用是反模式,但可以移动到componentWillMount.

我找到了原因

const mapStateToProps = (state) => {
  return {
    notifications: state.get("notifications").get("notifications").toJS()
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我没有返回通知,那就有效.但那是为什么呢?

import {connect} from "react-redux"
import {removeNotification, deactivateNotification} from "./actions"
import Notifications from "./Notifications.jsx"

const mapStateToProps = (state) => {
  return {
    notifications: state.get("notifications").get("notifications").toJS()
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    closeNotification: (notification) => {
      dispatch(deactivateNotification(notification.id))
      setTimeout(() => dispatch(removeNotification(notification.id)), 2000)
    }
  }
}

const NotificationsBotBot = connect(mapStateToProps, mapDispatchToProps)(Notifications)
export default NotificationsBotBot

import React from "react"

class Notifications extends React.Component {
  render() {
    return (
      <div></div>
    )
  }
}

export default Notifications
Run Code Online (Sandbox Code Playgroud)

UPDATE

在进一步的调试中我发现,以上可能不是根本原因,我可以让通知保持但我需要删除dispatch(push("/domains"))我的重定向.

这是我登录的方式:

export function doLogin (username, password) {
  return function (dispatch) {
    dispatch(loginRequest())
    console.log("Simulated login with", username, password)
    setTimeout(() => {
      dispatch(loginSuccess(`PLACEHOLDER_TOKEN${Date.now()}`))
      dispatch(addNotification({
        children: "Successfully logged in",
        type: "accept",
        timeout: 2000,
        action: "Ok"
      }))
      dispatch(push("/domains"))
    }, 1000)
  }
}
Run Code Online (Sandbox Code Playgroud)

我发现调度会引发警告,但为什么呢?我的域名页面目前没有任何内容:

import {connect} from "react-redux"
import DomainsIndex from "./DomainsIndex.jsx"

export default connect()(DomainsIndex)
Run Code Online (Sandbox Code Playgroud)

DomainsIndex

export default class DomainsIndex extends React.Component {
  render() {
    return (
      <div>
        <h1>Domains</h1>
      </div>
    )
  }
}
Run Code Online (Sandbox Code Playgroud)

更新2

我的App.jsx.<Notifications />显示通知的是什么

  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Layout>
        <Panel>
          <Switch>
            <Route path="/auth" />
            <Route component={TopBar} />
          </Switch>

          <Switch>
            <Route exact path="/" component={Index} />
            <Route path="/auth/login" component={LoginBotBot} />
            <AuthenticatedRoute exact path="/domains" component={DomainsPage} />
            <AuthenticatedRoute exact path="/domain/:id" component={DomainPage} />
            <Route component={Http404} />
          </Switch>
          <Notifications />
        </Panel>
      </Layout>
    </ConnectedRouter>
  </Provider>
Run Code Online (Sandbox Code Playgroud)

Fau*_* NA 6

您的dispatch(push('/domains'))其他调度设置了连接组件的状态(可能是关注通知的状态),在push生效后重新安装/卸载.

作为一种变通方法和概念验证,请尝试dispatch(push('/domains'))使用嵌套的零秒来延迟调用setTimeout.这应该push在任何其他操作完成之后执行(即希望渲染):

setTimeout(() => dispatch(push('/domains')), 0)
Run Code Online (Sandbox Code Playgroud)

如果可行,那么您可能需要重新考虑组件结构.我想Notifications是一个你想要挂载一次的组件,并在应用程序的生命周期内保留它.尽量避免通过将其放在组件树中更高的位置来重新安装它,并使其成为PureComponent(这里是文档).此外,如果应用程序的复杂性增加,您应该考虑使用库来处理像redux-saga这样的异步功能.

即使这个警告通常是由于错误的动作调用而出现的(例如调用渲染上的动作:onClick={action()}而不是作为lambda传递:) onClick={() => action()},如果你的组件看起来像你提到的那样(只是渲染a div),那么这不是原因这个问题.