Javascript Can';无法获取要更新的反应状态
我是个初学者,我觉得我在某个地方犯了一个根本性的错误。我正在制作一个简单的React组件,以便使用Tone JS按顺序播放音符。我不能用按钮更新笔记。当我点击一个按钮检查它们是否更新时,状态似乎已经改变,但Javascript Can';无法获取要更新的反应状态,javascript,reactjs,state,tonejs,Javascript,Reactjs,State,Tonejs,我是个初学者,我觉得我在某个地方犯了一个根本性的错误。我正在制作一个简单的React组件,以便使用Tone JS按顺序播放音符。我不能用按钮更新笔记。当我点击一个按钮检查它们是否更新时,状态似乎已经改变,但repeat功能仍然播放“旧笔记”。我哪里做错了 // create a synth let synth = new Tone.Synth({ attack: 0.5, decay: 0.5, sustain: 1, release: 5, volume: -10 }).t
repeat
功能仍然播放“旧笔记”。我哪里做错了
// create a synth
let synth = new Tone.Synth({
attack: 0.5,
decay: 0.5,
sustain: 1,
release: 5,
volume: -10
}).toDestination()
const Main = () => {
// set state with note values
const [noteValue, setNoteValue] = useState([
{ note: 'C4', properties: ''},
{ note: 'C4', properties: ''}
])
const [isPlaying, setIsPlaying] = useState(false)
// start/stop transport
const startStop = () => {
if (!isPlaying) Tone.start()
setIsPlaying(!isPlaying)
!isPlaying ? Tone.Transport.start() : Tone.Transport.stop()
}
Tone.Transport.bpm.value = 140
// song loop function - always displays the same notes after state change
let index = 0
function repeat(time){
const position = index % noteValue.length
const synthNote = noteValue[position]
console.log(noteValue)
synth.triggerAttackRelease(synthNote.note, time)
index++
}
// set the transport on the first render
useEffect(() => {
Tone.Transport.scheduleRepeat((time) => {
repeat(time)
}, '4n')
},[])
// the "change note values" button doesn't change them inside the repeat function
return <>
<button onClick={() => setNoteValue([
{ note: 'C5', properties: ''},
{ note: 'C5', properties: ''}
])}>Change Note Values</button>
<button onClick={() => console.log(noteValue)}>Check note values</button>
<button onClick={() => startStop()}>Start/Stop</button>
</>
//创建一个synth
让synth=new Tone.synth({
攻击:0.5,
衰减:0.5,
维持:1,,
发布日期:5,
卷:-10
}).toDestination()
常量Main=()=>{
//使用注释值设置状态
常量[noteValue,setNoteValue]=useState([
{注意:'C4',属性:'},
{注意:'C4',属性:'}
])
const[isplay,setisplay]=useState(false)
//启动/停止运输
常量startStop=()=>{
如果(!isplay)音调.start()
设置显示(!显示)
!isPlaying?Tone.Transport.start():Tone.Transport.stop()
}
Tone.Transport.bpm.value=140
//歌曲循环功能-状态更改后始终显示相同的音符
设指数=0
功能重复(时间){
常量位置=索引%noteValue.length
const syntnote=noteValue[位置]
console.log(noteValue)
synth.triggeratackrelease(synthNote.note,time)
索引++
}
//在第一次渲染时设置传输
useffect(()=>{
Tone.Transport.scheduleRepeat((时间)=>{
重复(时间)
},'4n')
},[])
//“更改注释值”按钮不会在repeat函数中更改它们
返回
setNoteValue([
{注意:'C5',属性:'},
{注意:'C5',属性:'}
])}>更改注释值
console.log(noteValue)}>检查注释值
startStop()}>开始/停止
谢谢。您的
useffect
在第一次渲染时执行一次。在该useffect
中,您计划从该范围引用repeat
函数的函数的重复。该repeat
函数引用noteValue
,这也是名为noteValue
的变量的值,该变量存在于第一次渲染的范围内
您的react state值实际上会发生变化,但由于您始终只引用effect中第一次执行的范围中的变量,因此您不会遇到任何这种情况
要查看它是否在实际更改,可以在代码中的某个位置添加console.log(noteValue)
要解决这个问题,您确实需要理解范围和闭包的概念。我推荐阅读
可能的解决办法:
一个可能的解决方案是让scheduleRepeat
返回一个unschedule
方法(无论如何,您需要这个方法,否则在卸载组件后声音会继续播放)。在这种情况下:
useffect(()=>{
功能重复(时间){
常量位置=索引%noteValue.length
const syntnote=noteValue[位置]
console.log(noteValue)
synth.triggeratackrelease(synthNote.note,time)
索引++
}
const unschedule=Tone.Transport.scheduleRepeat((时间)=>{
重复(时间)
},'4n')
非计划返回;
},[noteValue])
简而言之:repeat
被移动到useffect
中,useffect
获取对noteValue
的依赖项并返回清理回调。
否则,您将需要一个
useCallback
环绕repeat
的noteValue
作为依赖项,并添加repeat
作为useffect
的依赖项。通过这种方式,这两个功能集于一身。谢谢您的帮助。我读了你说的话,但我还是很困惑。我试着将函数中的变量移出它,但没有成功。我还注意到,当我从useffect
中删除第二个参数时,我可以将音符更改为播放,因此我也被这一点弄糊涂了。但是,删除此参数会导致Tone.transport
运行大量的时间,并导致播放大量的音符。将第二个参数更改为useEffect将导致它在其中一个依赖项更改时重新运行。但是,您也可以在重新运行效果之前进行清理,您需要在其中停止旧的运行效果。阅读推荐(当然除了react文档):谢谢你,弗瑞。我已经阅读了所有这些材料和React文档,感觉我已经尝试了所有的方法,但不幸的是,我不知道如何让它发挥作用。我在上面添加了一个示例解决方案。非常感谢您的解决方案。我认为这是有道理的(我确信这是有道理的,但我是一个初学者),但现在当我改变音符状态时,序列会在原始的基础上重复。你知道这是为什么吗?你能建议我如何解决这个问题吗?再次感谢您抽出时间