Javascript 反应警告:无法在现有状态转换期间更新(例如在“渲染”中)
我使用了Javascript 反应警告:无法在现有状态转换期间更新(例如在“渲染”中),javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我使用了ResetPassword组件来呈现Timer组件,下面是它们的代码- ResendPassword.js class ResetPassword extends Component{ constructor(props){ super(props); this.state = { resendActive: false }; } endHandler(){ this.set
ResetPassword
组件来呈现Timer
组件,下面是它们的代码-
ResendPassword.js
class ResetPassword extends Component{
constructor(props){
super(props);
this.state = {
resendActive: false
};
}
endHandler(){
this.setState({
resendActive: true
})
}
render(){
return (
<Timer sec={5} counter={this.state.counter} end={this.endHandler.bind(this)}/>
)
}
}
类重置密码扩展组件{
建造师(道具){
超级(道具);
此.state={
保留:错误
};
}
endHandler(){
这是我的国家({
保留:对
})
}
render(){
返回(
)
}
}
Timer.js
const Timer = (props) => {
const [sec, setSec] = useState(props.sec);
useEffect(() => {
setSec(props.sec);
const intr = setInterval(() => {
setSec((s) => {
if(s > 0)
return --s;
props.end(); // Line: causing warning
clearInterval(intr);
return s;
});
}, 1000)
return () => {
clearInterval(intr);
}
}, [props.counter])
return (
<span>{sec > 60 ? `${Math.floor(sec/60)}:${sec - Math.floor(sec/60)}`: `${sec}`} sec</span>
)
}
const Timer=(道具)=>{
常数[秒,设置秒]=使用状态(道具秒);
useffect(()=>{
设置秒(道具秒);
常量intr=setInterval(()=>{
setSec((s)=>{
如果(s>0)
return--s;
props.end();//行:导致警告
清除间隔(intr);
返回s;
});
}, 1000)
return()=>{
清除间隔(intr);
}
},[props.counter])
返回(
{sec>60?`${Math.floor(sec/60)}:${sec-Math.floor(sec/60)}`:${sec}}秒
)
}
在上面的代码中,我在ResetPassword中使用timer,我希望在计时器结束时调用函数,因此我将endHandler作为end-In-timer组件传递,但调用该函数时发出警告:无法在现有状态转换期间更新(例如在“render”中)
,有人能告诉我我这里做错了什么吗
提前感谢问题
setSec
是一种状态更新功能,您可以使用功能状态更新变量。此更新函数回调必须是纯函数,即零副作用。调用props.end()
是一种副作用
解决方案
将props.end
的副作用调用拆分为它自己的效果挂钩,使其独立于状态更新程序函数
const Timer = (props) => {
const [sec, setSec] = useState(props.sec);
useEffect(() => {
setSec(props.sec);
const intr = setInterval(() => {
setSec((s) => {
if (s > 0) return --s;
clearInterval(intr);
return s;
});
}, 1000);
return () => {
clearInterval(intr);
};
}, [props.counter]);
useEffect(() => {
console.log(sec);
if (sec <= 0) props.end(); // <-- move invoking `end` to own effect
}, [sec]);
return (
<span>
{sec > 60
? `${Math.floor(sec / 60)}:${sec - Math.floor(sec / 60)}`
: `${sec}`}{" "}
sec
</span>
);
};
更新计时器
以使用间隔挂钩
const Timer = ({ end, sec: secProp}) => {
const [sec, setSec] = useState(secProp);
// Only decrement sec if sec !== 0
useInterval(() => setSec((s) => s - (s ? 1 : 0)), 1000);
useEffect(() => {
!sec && end(); // sec === 0, end!
}, [sec, end]);
return (
<span>
{sec > 60
? `${Math.floor(sec / 60)}:${sec - Math.floor(sec / 60)}`
: `${sec}`}{" "}
sec
</span>
);
};
const Timer=({end,sec:secProp})=>{
常数[sec,setSec]=使用状态(secProp);
//仅当秒数!==0时减少秒数
使用间隔(()=>setSec((s)=>s-(s?1:0)),1000;
useffect(()=>{
!sec&&end();//sec==0,end!
},[sec,end]);
返回(
{秒>60
?`${Math.floor(sec/60)}:${sec-Math.floor(sec/60)}`
:`${sec}`}{'}
秒
);
};
为什么需要将道具存储在本地组件状态?这是一个反模式。为什么不直接使用
setTimeout
?它的用例更像您描述的,在react钩子中管理起来更容易。@DrewReese计时器组件渲染秒数,每秒钟减少1秒,从父级运行不需要几秒,所以我必须将从父级获得的秒数设置为状态,然后在每次调用RERERENDER的setInterval时将其递减。递减秒计时器就像倒计时10,9,8。。。直到0是的,这一个有效,但我不明白为什么我的一个是给予warning@AbhaySehgal你完全正确,我只是做了我最讨厌的事情之一,提供了一个答案,但忽略了解决OP问题。对于您的原始代码,我可以将问题追溯到状态更新函数,它不会有副作用。将副作用移入其自身的效果挂钩似乎可以解决警告问题。我已经更新了我的答案。
const Timer = ({ end, sec: secProp}) => {
const [sec, setSec] = useState(secProp);
// Only decrement sec if sec !== 0
useInterval(() => setSec((s) => s - (s ? 1 : 0)), 1000);
useEffect(() => {
!sec && end(); // sec === 0, end!
}, [sec, end]);
return (
<span>
{sec > 60
? `${Math.floor(sec / 60)}:${sec - Math.floor(sec / 60)}`
: `${sec}`}{" "}
sec
</span>
);
};