首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >React setState未更新状态;未调用回调

React setState未更新状态;未调用回调
EN

Stack Overflow用户
提问于 2017-12-08 06:02:07
回答 1查看 699关注 0票数 0

我正在尝试将我在"Tale of Two Clocks" tutorial之后构建的节拍器转换为一个简单的React项目。我认为绘制节拍器的参数(节拍、音符分区等)非常容易。并对其进行扩展,以构建一个步序发生器(科技万岁!)。这就是说,我在写一篇文章时遇到了问题,我想知道为什么。

我在Metronome组件上获得了几乎所有重要的部分,如下所示:

代码语言:javascript
运行
复制
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()调用替换掉了普通的JS变量重新赋值/递增。在相当多的情况下,我需要使用状态中的更新值进行计算,并且知道setState()是异步的,并且经常对调用进行批处理,所以我在某种程度上严重依赖于setState接受的可选回调。

有一个地方似乎出了问题,那就是nextNote()方法。状态永远不会正确更新:除了在play()方法中分配的值之外,this.state.nextNoteTime永远不会获得更新值,代码原样将永远不会进入从nextNote()调用的传递给setState()的回调中。如果我从组件的状态中删除nextNoteTime,并将其保存为组件的一个属性,手动更新和跟踪它的值,节拍器似乎可以按预期工作,就像我的节拍器在vanilla JS中的结束状态一样。从技术上讲是成功的,但不是我想要的答案。

我尝试使用Promises,并尝试使用React组件API (componentWillUpdateshouldComponentUpdate等)。来确定下一个状态是否是我正在寻找的状态。但是超过了某个点(比如3次scheduler()方法的递归调用),while循环就会变得混乱,因为this.state.nextNoteTime从来不会增加。啊!

有什么想法?

EN

回答 1

Stack Overflow用户

发布于 2017-12-08 08:06:30

我想这是因为你嵌套了setState

尝试下面的播放方法。

代码语言:javascript
运行
复制
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)
    }
  });
}

同时删除nextNotesetState嵌套。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47704568

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档