[英]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( componentWillUpdate
, shouldComponentUpdate
等)来确定下一个状态是否是我要寻找的状态。 但是超出某个点(例如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.