簡體   English   中英

反應setState不更新狀態; 回調未被調用

[英]React setState not updating state; callback not being called

我正在嘗試按照“兩個時鍾的故事”教程構建的節拍器,並將其轉換為簡單的React項目。 我認為映射節拍器的參數(速度,音符划分等)並對其進行擴展以構建步進音序器(對技術而言萬歲!)非常容易。 也就是說,我在處理特定作品時遇到麻煩,我想知道為什么。

我在Metronome組件上幾乎擁有所有重要的部分,例如:

import React from 'react'

class Metronome extends React.Component {
  constructor (props) {
    super(props)

    this.audioContext = new (window.AudioContext || window.webkitAudioContext)()

    this.state = {
      tempo: 120.0,
      noteResolution: 0,
      isPlaying: false,
      current16thNote: 0,
      gateLength: 0.05,
      noteLength: 0.25,
      nextNoteTime: 0.0
    }

    this.lookahead = 0.1
    this.schedulerInterval = 25.0
    this.timerID = null

    this.updateStartStopText = this.updateStartStopText.bind(this)
    this.play = this.play.bind(this)
    this.scheduler = this.scheduler.bind(this)
    this.scheduleNote = this.scheduleNote.bind(this)
    this.nextNote = this.nextNote.bind(this)
  }

  updateStartStopText () {
    return this.state.isPlaying ? 'Stop' : 'Start'
  }

  play () {
    this.setState({ isPlaying: !this.state.isPlaying }, () => {
      if (this.state.isPlaying) {
        this.setState({ current16thNote: 0 }, () => {
          this.setState({
            nextNoteTime: this.audioContext.currentTime
          }, this.scheduler)
        })
      } else {
        window.clearTimeout(this.timerID)
      }
    })
  }

  scheduler () {
    while (this.state.nextNoteTime < this.audioContext.currentTime + this.lookahead) {
      this.scheduleNote(this.state.current16thNote, this.state.nextNoteTime)
      this.nextNote()
    }

    this.timerID = window.setTimeout(this.scheduler, this.schedulerInterval)
  }

  scheduleNote (beatNumber, time) {
    if ((this.state.noteResolution === 1) && (beatNumber % 2)) {
      return
    } else if ((this.state.noteResolution === 2) && (beatNumber % 4)) {
      return
    }
    const osc = this.audioContext.createOscillator()
    osc.connect(this.audioContext.destination)
    if (!(beatNumber % 16)) {
      osc.frequency.value = 220
    } else if (beatNumber % 4) {
      osc.frequency.value = 440
    } else {
      osc.frequency.value = 880
    }

    osc.start(time)
    osc.stop(time + this.state.gateLength)
  }

  nextNote () {
    var secondsPerBeat = 60.0 / this.state.tempo
    let nextTime = this.state.nextNoteTime + (secondsPerBeat * this.state.noteLength)

    this.setState({
      nextNoteTime: nextTime,
      current16thNote: (this.state.current16thNote + 1)
    }, () => {
      if (this.state.current16thNote === 16) {
        this.setState({ current16thNote: 0 })
      }
    })
  }

  render () {
    return (
      <div>
        Metronome

        <button type='button' onClick={this.play}>{this.updateStartStopText()}</button>
      </div>
    )
  }
}

export default Metronome

或多或少的一切都會像React所期望的那樣進行,用this.setState()調用替換this.setState() JS變量重新分配/遞增。 在很多情況下,我需要使用狀態中的更新值進行計算,並且知道setState()是異步的並且經常分批調用,因此我在某種程度上依賴於setState接受的可選回調。

事情似乎出現問題的一個地方是nextNote()方法。 狀態永遠不會正確更新: this.state.nextNoteTime永遠不會獲得超出play()方法中分配的值的更新值。代碼本身永遠不會進入傳遞給從nextNote()調用的setState()的回調中。 如果我從組件的狀態中刪除nextNoteTime並將其保存為組件上的屬性,手動更新並跟蹤其值,則節拍器似乎可以按預期工作,就像我在香草JS中的節拍器的最終狀態一樣。 從技術上講是成功的,但不是我要尋找的答案。

我嘗試使用Promises並嘗試使用React Component API( componentWillUpdateshouldComponentUpdate等)來確定下一個狀態是否是我要尋找的狀態。 但是超出某個點(例如scheduler()方法的3次遞歸調用)之后,while循環變得很麻煩,因為this.state.nextNoteTime永遠不會增加。 加!

有什么想法嗎?

我認為這是因為您嵌套了setState

請嘗試以下播放方法。

play() {
  const { isPlaying } = this.state;
  const newState = { isPlaying: !isPlaying };
  if (!isPlaying) {
    Object.assign(newState, {
      current16thNote: 0,
      nextNoteTime: this.audioContext.currentTime
    });
  }

  this.setState(newState, (...args) => {
    if (!isPlaying) {
      this.scheduler(...args);
    } else {
      window.clearTimeout(this.timerID)
    }
  });
}

nextNote刪除nextNote setState嵌套。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM