Javascript 设置不更新变量的间隔

Javascript 设置不更新变量的间隔,javascript,reactjs,react-hooks,setinterval,Javascript,Reactjs,React Hooks,Setinterval,我试图捕获用户的点击次数。 我希望每15分钟发送一次api, 我正在使用setinterval inside useEffect来实现这一点,但问题是即使状态在setinterval外部而不是内部发生变化。setinterval仅提供初始默认值 这是密码- const Agent = (props: RouteComponentProps) => { const [clicks, setClicks] = useState(0); const handleOnIdle = ()

我试图捕获用户的点击次数。 我希望每15分钟发送一次api, 我正在使用setinterval inside useEffect来实现这一点,但问题是即使状态在setinterval外部而不是内部发生变化。setinterval仅提供初始默认值

这是密码-

const Agent = (props: RouteComponentProps) => {
  const [clicks, setClicks] = useState(0);

  const handleOnIdle = () => {
    console.log("user is idle");
    console.log("last active", new Date(getLastActiveTime()));
    console.log("total idle time: ", getTotalIdleTime() / 1000);
  };

  const handleOnActive = () => {
    console.log("user is active");
    console.log("time remaining", getRemainingTime());
  };

  const handleOnAction = () => {
    console.log("user did something", clicks);
    setClicks(clicks + 1);
  };

  const {
    getRemainingTime,
    getLastActiveTime,
    getTotalIdleTime,
  } = useIdleTimer({
    timeout: 10000,
    onIdle: handleOnIdle,
    onActive: handleOnActive,
    onAction: handleOnAction,
    debounce: 500,
  });

  useEffect(() => {
    let timer = setInterval(
      () => alert(`user clicked ${clicks} times`),
      1000 * 30
    );

    return () => {
      clearInterval(timer);
      setClicks(0);
    };
  }, []);

  return (
    <AgentLayout>
      <div className="dashboard-wrapper py-3">
        <Switch>
          <Redirect
            exact
            from={`${props.match.url}/`}
            to={`${props.match.url}/home`}
          />
          <Route path={`${props.match.url}/home`} component={Home} />
          <Route
            path={`${props.match.url}/lead-details`}
            component={leadDetails}
          />
          <Route
            path={`${props.match.url}/fill-details`}
            component={FillDetails}
          />
          <Route
            path={`${props.match.url}/my-desktime`}
            component={MyDesktime}
          />

          <Redirect to="/error" />
        </Switch>
      </div>
    </AgentLayout>
const-Agent=(props:RouteComponentProps)=>{
const[clicks,setClicks]=useState(0);
常量handleOnIdle=()=>{
log(“用户空闲”);
log(“上次激活”,新日期(getLastActiveTime());
log(“总空闲时间:”,getTotalDeleteTime()/1000);
};
const handleOnActive=()=>{
console.log(“用户处于活动状态”);
log(“剩余时间”,getRemainingTime());
};
常量操作=()=>{
log(“用户做了什么”,点击);
设置单击次数(单击次数+1);
};
常数{
getRemainingTime,
getLastActiveTime,
GetTotaleTime,
}=useIdleTimer({
超时:10000,
onIdle:handleOnIdle,
onActive:handleon主动,
行动:行动,
去盎司:500,
});
useffect(()=>{
让定时器=设置间隔(
()=>警报(`user clicked${clicks}次`),
1000 * 30
);
return()=>{
清除间隔(计时器);
setClicks(0);
};
}, []);
返回(

该警报提示用户单击了0次

问题是计时器函数仅使用第一次
单击的版本,因为它会在第一次调用组件函数时关闭该版本。当您将空依赖项数组传递给
useffect
时,
useEffect
回调只调用一次,即第一次调用组件函数时(安装组件时)

您可以通过让计时器功能改用
setClicks
方法来解决此问题:

useEffect(() => {
    let timer = setInterval(
        () => {
            setClicks(currentClicks => {                      // ***
                alert(`user clicked ${currentClicks} times`); // ***
                return currentClicks;                         // ***
            });                                               // ***
        },
        1000 * 30
    );

    return () => {
        clearInterval(timer);
        // setClicks(0); // <=== Don't do this, the component is unmounting
    };
}, []);
这基本上可以正常工作,但每次
单击
更改时,它都会重新启动计时器,即使这意味着它不会等待30秒,而是等待45秒(因为
单击
在15秒后更改,从而取消了上一个间隔并再次启动)。对于很短的时间间隔来说,这可能无关紧要,但对于30秒的时间间隔来说,这似乎不太理想。这样做而不扰乱时间间隔的计时要求您记住下一次计时器回调应该发生的时间,并调整初始延迟的持续时间以匹配,这变得相当复杂


我之前没有注意到,但是任何时候你根据现有状态更新状态,我建议使用回调表单

const handleOnAction = () => {
    setClicks(clicks => clicks + 1); // Note the function callback
};
问题
  • 您有一个陈旧的
    单击
    状态的封闭空间,从装载效果运行并开始间隔时的初始渲染周期开始关闭
  • 您的点击更新程序应该使用功能更新来更新上一状态的点击次数。这包括用户点击速度是否比react组件生命周期快,以及在一个渲染周期中将多个点击次数排队
  • T.J.的回答是好的,但是我认为 UsStuts<代码>更新函数是一个纯函数,并且<代码>警报(‘用户点击${CurrutsCouk} Times’);在中间是一个副作用,(imHo)应该避免。 解决方案 更新

    handleOnAction
    以使用功能状态更新

    const handleOnAction = () => {
      console.log("user did something", clicks);
      setClicks(clicks => clicks + 1);
    };
    
    我建议使用React ref和附加的
    useffect
    来更新它,为要引用的间隔回调保存
    单击
    状态的缓存副本

    const clicksRef = useRef();
    
    useEffect(() => {
      clicksRef.current = clicks; // update clicks ref when clicks updates
    }, [clicks]);
    
    useEffect(() => {
      const timer = setInterval(
        () => alert(`user clicked ${clicksRef.current} times`), // use clicks ref instead
        1000 * 30
      );
    
      return () => {
        clearInterval(timer);
        // TJ already covered removing this extra state update
      };
    }, []);
    

    @MohammadUbaid-很高兴这有帮助!您应该删除清理函数中的
    setClicks(0);
    调用,我已经更新以显示这一点。在我们传递的版本中
    []
    useffect
    ,卸载组件时会调用cleanup函数,因此不应设置状态。在传递
    [单击]的版本中
    ,每次
    单击
    更改时都会调用cleanup函数,因此我们确实不应该在那里更新
    单击
    :-D@t-j-crowder我还有一个问题。介意看一看吗?@MohammadUbaid-我现在很忙,但可能晚些时候。你发了吗?@t-j-crowder是的,刚刚。谢谢,伙计!我已经解决了
    const clicksRef = useRef();
    
    useEffect(() => {
      clicksRef.current = clicks; // update clicks ref when clicks updates
    }, [clicks]);
    
    useEffect(() => {
      const timer = setInterval(
        () => alert(`user clicked ${clicksRef.current} times`), // use clicks ref instead
        1000 * 30
      );
    
      return () => {
        clearInterval(timer);
        // TJ already covered removing this extra state update
      };
    }, []);