Предупреждение компонента React setState(): можно обновить только смонтированный или смонтированный компонент

Я пытаюсь отобразить таймер обратного отсчета, используя следующий код компонента, но я продолжаю получать предупреждение.

"Предупреждение 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.


person jasan    schedule 20.10.2016    source источник
comment
При каком вызове четверки на setState это происходит?   -  person Sami Kuhmonen    schedule 20.10.2016
comment
Ну, это говорит вам, что не так, я думаю: вы используете setState в componentWillMount, когда компонент еще не смонтирован. Я сомневаюсь, что вам нужно установить состояние там в первую очередь.   -  person Vincas Stonys    schedule 20.10.2016
comment
переместить его в componentDidMount   -  person abhirathore2006    schedule 20.10.2016
comment
перемещение setState в componentDidMount() останавливает работу счетчика, и я получаю ошибку lint, которая не использует setState в componentDidMount. И событие, если я удалю this.setState({isMounted: true}), я все равно получу ошибку   -  person jasan    schedule 20.10.2016


Ответы (1)


Вы буквально делаете .setState() до того, как компонент будет смонтирован (componentWillMount()). Вы не можете этого сделать, потому что компонента еще не существует. Скорее поместите это в constructor() в качестве начального состояния.

person Fabian Schultz    schedule 20.10.2016
comment
перемещение setState в componentDidMount() останавливает работу счетчика, и я получаю ошибку lint, которая не использует setState в componentDidMount - person jasan; 20.10.2016
comment
Да, извините, поместите это в constructor() как this.state = {}. Это начальное состояние. - person Fabian Schultz; 20.10.2016
comment
После дальнейшего тестирования я не могу воспроизвести вашу проблему: jsbin.com/wihizi/edit?js,output Установка состояния в componentDidMount также не является анти-шаблоном, поэтому не знаю, почему вашему линтеру это не нравится. - person Fabian Schultz; 20.10.2016