【问题标题】:React Component Warning setState(...): Can only update a mounted or mounting componentReact Component 警告 setState(...):只能更新已安装或正在安装的组件
【发布时间】:2017-03-02 11:03:02
【问题描述】:

我正在尝试使用以下组件代码显示倒数计时器,但我不断收到警告。

"警告 setState(...): 只能更新一个挂载或挂载 组件”

但是,我假设我只是在检查我的 isMounted 状态变量在整个组件中是否为真之后才设置状态。我的错误在哪里?

import React, { Component, PropTypes } from 'react'

export default class CountDownTimerContainer extends Component {
  static propTypes = {
    initialTimeRemaining: PropTypes.number.isRequired,
    interval: PropTypes.number,
    formatFunc: PropTypes.func,
    tickCallback: PropTypes.func,
    completeCallback: PropTypes.func
  }

  constructor (props) {
    super(props)
    this.state = {
      timeRemaining: this.props.initialTimeRemaining,
      timeoutId: null,
      prevTime: null,
      isMounted: false
    }
  }

  componentWillMount () {
    this.setState({isMounted: true})
  }

  componentDidMount () {
    this.tick()
  }

  componentWillReceiveProps (newProps) {
    if (this.state.timeoutId) {
      clearTimeout(this.state.timeoutId)
    }
    if (this.state.isMounted) {
      this.setState({prevTime: null, timeRemaining: newProps.initialTimeRemaining})
    }
  }

  componentDidUpdate () {
    if ((!this.state.prevTime) && this.state.timeRemaining > 0 && this.state.isMounted) {
      this.tick().bind(this)
    }
  }

  componentWillUnmount () {
    this.setState({isMounted: false})
    clearTimeout(this.state.timeoutId)
  }

  tick () {
    const currentTime = Date.now()
    const dt = this.state.prevTime ? (currentTime - this.state.prevTime) : 0
    const interval = this.props.interval

    // correct for small variations in actual timeout time
    const timeRemainingInInterval = (interval - (dt % interval))
    let timeout = timeRemainingInInterval

    if (timeRemainingInInterval < (interval / 2.0)) {
      timeout += interval
    }

    const timeRemaining = Math.max(this.state.timeRemaining - dt, 0)
    const countdownComplete = (this.state.prevTime && timeRemaining <= 0)

    if (this.state.isMounted) {
      if (this.state.timeoutId) {
        clearTimeout(this.state.timeoutId)
      }
      this.setState({
        timeoutId: countdownComplete ? null : setTimeout(this.tick.bind(this), timeout),
        prevTime: currentTime,
        timeRemaining: timeRemaining
      })
    }

    if (countdownComplete) {
      if (this.props.completeCallback) {
        this.props.completeCallback()
      }
      return
    }

    if (this.props.tickCallback) {
      this.props.tickCallback(timeRemaining)
    }
  }

  getFormattedTime (milliseconds) {
    if (this.props.formatFunc) {
      return this.props.formatFunc(milliseconds)
    }

    var totalSeconds = Math.round(milliseconds / 1000)

    var seconds = parseInt(totalSeconds % 60, 10)
    var minutes = parseInt(totalSeconds / 60, 10) % 60
    var hours = parseInt(totalSeconds / 3600, 10)

    seconds = seconds < 10 ? '0' + seconds : seconds
    minutes = minutes < 10 ? '0' + minutes : minutes
    hours = hours < 10 ? '0' + hours : hours

    return hours + ':' + minutes + ':' + seconds
  }

  render () {
    var timeRemaining = this.state.timeRemaining
    return (
      <div className='timer'>
        {this.getFormattedTime(timeRemaining)}
      </div>
    )
  }
}

CountDownTimerContainer.defaultProps = {
  interval: 1000,
  formatFunc: null,
  tickCallback: null,
  completeCallback: null
}

更新:即使在删除之后

this.setState({isMounted: true}) 

来自 ComponentWIllMount 并将其作为 this.state({isMounted: true}) 添加到构造函数中,我仍然得到 setStateError。

【问题讨论】:

  • 四人中哪一次调用setState会发生这种情况?
  • 它告诉你我认为哪里出了问题:当组件尚未安装时,你在componentWillMount 中使用setState。我怀疑您是否需要首先在那里设置状态。
  • 把它移到componentDidMount
  • 将 setState 移动到 componentDidMount() 会使计数器停止工作并且我收到了不要在 componentDidMount 中使用 setState 的 lint 错误。如果我删除 this.setState({isMounted: true}) 事件,我仍然会收到错误

标签: reactjs redux setstate


【解决方案1】:

在安装组件之前,您确实做了一个.setState() (componentWillMount())。您不能这样做,因为该组件尚不存在。而是将其放在constructor() 中作为初始状态。

【讨论】:

  • 将 setState 移动到 componentDidMount() 会使计数器停止工作并且我收到了不要在 componentDidMount 中使用 setState 的 lint 错误
  • 是的,对不起,把它放在constructor() 中作为this.state = {}。这是初始状态。
  • 经过进一步测试,我无法重现您的问题:jsbin.com/wihizi/edit?js,outputcomponentDidMount 中设置状态也不是反模式,所以不确定为什么您的 linter 不喜欢它.
猜你喜欢
  • 2016-10-13
  • 1970-01-01
  • 2017-04-26
  • 1970-01-01
  • 2019-08-15
  • 1970-01-01
  • 2016-09-25
  • 1970-01-01
  • 2018-03-26
相关资源
最近更新 更多