Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 反应警告:无法在现有状态转换期间更新(例如在“渲染”中)_Javascript_Reactjs_React Hooks - Fatal编程技术网

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>
  );
};