【发布时间】:2021-06-15 16:03:43
【问题描述】:
以下是导致我遇到麻烦的部分的代码分解。
我定义了一个类组件来存储状态数据,"timerState",这是本例的主要焦点,将在真假之间切换
this.state={
brkLength:1,
sesnLength:1,
timer:60,
timerState:'false',
timerType:'Session',
}
handleTimer 函数将在 onclick 事件发生后启动。由于 setstate 是异步运行的,我不希望在状态突变之前调用函数-timeCountDown 和 breakCountDown,所以我将它们设置为 setState 的回调函数。
handleTimer(){
console.log(this.state.timerState)
if(this.state.timerType=="Session"){
this.setState({
timerState:!this.state.timerState
},()=>this.timeCountDown())
}else if(this.state.timerType=="Break"){
this.setState({
timerState:!this.state.timerState
},()=>this.breakCountDown())
}
}
然而,正如 console.log 在两个地方显示的那样——一个在 handleTimer 另一个在 timeCountDown,它们都打印“ 错误”。
timeCountDown(){
console.log(this.state.timerState)
if(this.state.timerState){
this.myCountDown=setInterval(()=>{
if(this.state.timer>0){
this.setState(prevState=>({
timer:prevState.timer-1
}))
}else if(this.state.timer<=0){
clearInterval(this.myCountDown)
this.soundPlay()
this.setState({
timerType:'Break',
timer:this.state.brkLength*60,
},()=>this.breakCountDown())
}
}
,1000)
}else{
clearInterval(this.myCountDown)
}
}
我想知道上面的代码 sn-p 出了什么问题。 link 来了,如果您想查看它,可以查看整个编码。
function formatTime(time){
let minutes=Math.floor(time/60)
let seconds=time%60
minutes=minutes<10?"0"+minutes:minutes
seconds=seconds<10?"0"+seconds:seconds
return minutes +":"+seconds
}
const TimerLengthControl=(props)=>(
<div className="LengthContainer">
<div className="controlTitle" id={props.titleID}>{props.title}</div>
<div>
<button
id={props.decrementID}
value="-1"
type={props.type}
onClick={props.onClick}
>
<i className="fas fa-arrow-down"></i>
</button>
<span id={props.spanID}>{props.span}</span>
<button
id={props.incrementID}
value="+1"
type={props.type}
onClick={props.onClick}
>
<i className="fas fa-arrow-up"></i>
</button>
</div>
</div>
)
const TimerControl=(props)=>(
<div className="timerControlContainer">
<div className="timerContainer">
<div id="timer-label">{props.timerType}</div>
<div id="time-left">{formatTime(props.timeLeft)}</div>
</div>
<div className="buttonContainer">
<button id="start_stop" onClick={props.timerHandler}>
<i className="fas fa-play"/>
<i className="fas fa-pause"/>
</button>
<button id="reset">
<i className="fas fa-sync" onClick={props.resetHandler}/>
</button>
</div>
</div>
)
class App extends React.Component{
constructor(){
super()
this.state={
brkLength:1,
sesnLength:1,
timer:60,
timerState:'false',
timerType:'Session',
}
this.handleReset=this.handleReset.bind(this)
this.handleOperation=this.handleOperation.bind(this)
this.handleBreakLength=this.handleBreakLength.bind(this)
this.handleSessionLength=this.handleSessionLength.bind(this)
this.handleTimer=this.handleTimer.bind(this)
};
handleReset(){
clearInterval(this.myCountDown)
this.setState({
brkLength:5,
sesnLength:25,
timer:1500,
timerState:'false',
timerType:'Session',
})
}
timeCountDown(){
console.log(this.state.timerState)
if(this.state.timerState){
this.myCountDown=setInterval(()=>{
if(this.state.timer>0){
this.setState(prevState=>({
timer:prevState.timer-1
}))
}else if(this.state.timer<=0){
clearInterval(this.myCountDown)
this.soundPlay()
this.setState({
timerType:'Break',
timer:this.state.brkLength*60,
},()=>this.breakCountDown())
}
}
,1000)
}else{
clearInterval(this.myCountDown)
}
}
soundPlay(){
const audio= new Audio("https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav")
audio.play()
}
breakCountDown(){
if(this.state.timerState){
this.myCountDown=setInterval(()=>{
if(this.state.timer>0){
this.setState({timer:this.state.timer-1})
}else if(this.state.timer<=0){
clearInterval(this.myCountDown)
this.soundPlay()
this.setState({
timerType:'Session',
timer:this.state.sesnLength*60
})
}
},1000)
}else{
clearInterval(this.myCountDown)
}
}
handleTimer(){
console.log(this.state.timerState)
if(this.state.timerType=="Session"){
this.setState({
timerState:!this.state.timerState
},()=>this.timeCountDown())
}else if(this.state.timerType=="Break"){
this.setState({
timerState:!this.state.timerState
},()=>this.breakCountDown())
}
}
handleOperation(stateToChange,amount){
const breakLength=this.state.brkLength
const sessionLength=this.state.sesnLength
if(stateToChange=="sesnLength"&&sessionLength==1&&amount<0){
return
}else if(stateToChange=="sesnLength"&&sessionLength==60&&amount>0){
return
}else if(stateToChange=="sesnLength"){
this.setState({
[stateToChange]:this.state[stateToChange]+Number(amount)*1,
timer:this.state.timer+Number(amount)*60
})
}
if(stateToChange=="brkLength"&&breakLength==1&&amount<0){
return
}else if (stateToChange=="brkLength"){
this.setState({[stateToChange]:this.state[stateToChange]+Number(amount)})
}
}
handleBreakLength(e){
const {value}=e.currentTarget
const type="brkLength"
this.handleOperation(type,value)
}
handleSessionLength(e){
const {value}=e.currentTarget
const type="sesnLength"
this.handleOperation(type,value)
}
render(){
return(
<div>
<TimerLengthControl
title="Break Length"
titleID="break-label"
decrementID="break-decrement"
incrementID="break-increment"
spanID="break-length"
span={this.state.brkLength}
onClick={this.handleBreakLength}
/>
<TimerLengthControl
title="Session Length"
titleID="session-label"
decrementID="session-decrement"
incrementID="session-increment"
spanID="session-length"
span={this.state.sesnLength}
onClick={this.handleSessionLength}
/>
<TimerControl
timeLeft={this.state.timer}
resetHandler={this.handleReset}
timerHandler={this.handleTimer}
timerType={this.state.timerType}
/>
</div>
);
}
}
ReactDOM.render(<App />,document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
</head>
<body>
<div id="root">
</div>
</body>
</html>
【问题讨论】:
-
请注意,每当根据现有状态 (
this.setState({timerState: !this.state.timerState}, /*...*/);) 更新状态时,使用setState的回调形式几乎总是更可靠:this.setState(({timerState}) => ({timerState: !timerState}), /*...*/);。这是因为状态更新的方式可以是batched together。这可能很好地解释了您的问题,但我需要一个可运行的示例来查看。 -
(为什么不只看链接的?因为 SO 的工作方式,你的整个问题 [包括任何必要的代码] 必须 in 你的问题,而不仅仅是链接. 三个原因:人们不应该去场外帮助您;某些网站对某些用户被屏蔽;链接失效,使问题及其答案对未来的人们毫无用处。)
-
@T.J.克劳德感谢您的回复。我只是通过 stack sn-p 包含了上面的所有代码。现在是一个带有最小可重现示例的问题吗?我对这个社区很陌生,有时我忘记了应该严格遵守的原则。
-
是的,太好了!虽然它不是真的最小,但似乎有很多你不需要证明问题的地方。
标签: javascript html reactjs setstate