简体   繁体   English

React useState 重置为未定义,我不知道为什么

[英]React useState is reset to undefined and I have no idea why

I am still very new to react and I ran into the following problem:我仍然很陌生,我遇到了以下问题:
I created a component which will contains a watchPosition method and an interval.我创建了一个包含 watchPosition 方法和间隔的组件。 This component will get re-rendered very often but I don't want the interval and the watchPosition method to get re-created every time.这个组件会经常重新渲染,但我不希望每次都重新创建间隔和 watchPosition 方法。 My solution is to store the ids of the interval and watchPosition in useState so that a new interval/watchPosition only gets created if the states are undefined.我的解决方案是将间隔和 watchPosition 的 id 存储在 useState 中,这样只有在状态未定义时才会创建新的间隔/watchPosition。 This is almost working :D这几乎可以工作了:D
This is my code (I added some console outputs to see what's happening):这是我的代码(我添加了一些控制台输出以查看发生了什么):

import { useState } from "react";
import "./styles.css";

export default function App() {
  console.log("NEW RENDER");
  
  const [watcherId, setWatcherId] = useState();
  const [updatingInterval, setUpdatingInterval] = useState();
  //test is a dummy variable to manually create re-rendering and check if new intervals get created
  const [test, setTest] = useState(1);

  async function initializeContext() {
    console.log("current value of updatingInterval: ", updatingInterval);
    if (updatingInterval === undefined) {
      console.log("new interval created");
      setUpdatingInterval(setInterval(updateValues, 1000));
    }
    console.log("current value of watcherId: ", watcherId);
    if (watcherId === undefined) {
      console.log("new geoWatcher created");
      setWatcherId(navigator.geolocation.watchPosition(success, error, {
        maximumAge: 500,
        enableHighAccuracy: true
      }));
    }
  }

  function success() {
    console.log("executed!");
  }

  function error(error) {
    console.error("error: ", error);
  }

  function buttonFunction() {
    setTest((prevText) => {
      return prevText + 1;
    });
  }

  function updateValues() {
    console.log("Interval executed");
  }

  initializeContext();
  return (
    <div className="App">
      <h1>{test}</h1>
      <button onClick={buttonFunction}>Switch</button>
    </div>
  );
}

This is the corresponding console log:这是相应的控制台日志: 在此处输入图像描述

As you can see for some reason the values of the two states are set to 16 and 1 and in the next execution they are undefined again.正如您所看到的,由于某种原因,这两个状态的值被设置为 16 和 1,并且在下一次执行中它们再次未定义。 After that they are set to 25 and 2 and from this time on everything works as expected.之后,它们被设置为 25 和 2,从那时起,一切都按预期工作。
I have no idea what I did wrong.我不知道我做错了什么。

You should not call states directly into the rendering function.您不应该将状态直接调用到渲染函数中。 That may cause infinite re-renderings.这可能会导致无限的重新渲染。 For example, you update states > related components get re-rendered > states will be updated again (because state update will be called in the rendering again).例如,您更新状态 > 相关组件重新渲染 > 状态将再次更新(因为状态更新将在渲染中再次调用)。

According to your logic, seemingly, you want to update states only once after the component App gets rendered.根据您的逻辑,您似乎只想在组件App渲染后更新状态一次。 In that case, I'd suggest you use useEffect instead.在这种情况下,我建议您改用useEffect

Furthermore, you should use useRef for the interval and watcherId.此外,您应该使用useRef作为间隔和 watcherId。 That would help you to reduce useless renderings.这将帮助您减少无用的渲染。

import { useState, useEffect, useRef } from "react";
import "./styles.css";

export default function App() {
  console.log("NEW RENDER");
  
  const watcherIdRef = useRef();
  const updatingIntervalRef = useRef();
  //test is a dummy variable to manually create re-rendering and check if new intervals get created
  const [test, setTest] = useState(1);

  //only called in the first rendering
  useEffect(() => {
    initializeContext();
  }, [])

  function initializeContext() {
    //clean up the interval
    if(updatingIntervalRef.current) {
       clearInterval(updatingIntervalRef.current)
    }
    
    //no need to have undefined check
    updatingIntervalRef.current = setInterval(updateValues, 1000);
    watcherIdRef.current = navigator.geolocation.watchPosition(success, error, {
        maximumAge: 500,
        enableHighAccuracy: true
    });
  }

  function success() {
    console.log("executed!");
  }

  function error(error) {
    console.error("error: ", error);
  }

  function buttonFunction() {
    setTest((prevText) => {
      return prevText + 1;
    });
  }

  function updateValues() {
    console.log("Interval executed");
  }

  return (
    <div className="App">
      <h1>{test}</h1>
      <button onClick={buttonFunction}>Switch</button>
    </div>
  );
}

Testable version可测试版本

 const { useState, useEffect, useRef } = React; function App() { console.log("NEW RENDER"); const watcherIdRef = useRef(); const updatingIntervalRef = useRef(); //test is a dummy variable to manually create re-rendering and check if new intervals get created const [test, setTest] = useState(1); //only called in the first rendering useEffect(() => { initializeContext(); }, []); function initializeContext() { console.log("current value of updatingInterval: ", updatingIntervalRef.current); //clean up the interval if(updatingIntervalRef.current) { clearInterval(updatingIntervalRef.current) } //no need to have undefined check updatingIntervalRef.current = setInterval(updateValues, 1000); watcherIdRef.current = navigator.geolocation.watchPosition(success, error, { maximumAge: 500, enableHighAccuracy: true }); } function success() { console.log("executed!"); } function error(error) { console.error("error: ", error); } function buttonFunction() { setTest((prevText) => { return prevText + 1; }); } function updateValues() { console.log("Interval executed"); } return ( <div className="App"> <h1>{test}</h1> <button onClick={buttonFunction}>Switch</button> </div> ); } ReactDOM.render( <App/>, document.getElementById("root") );
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="root"></div>

remove initializeContext() from function ,You should call this in useEffect从函数中删除 initializeContext() ,你应该在 useEffect 中调用它

useEffect(()=>{
  initializeContext();
}, [])

Not in function component.不在功能组件中。 When it prints values it is when the setIterval is executing the function, and it prints undefined when the function is not called (meanwhile)当它打印值时,它是当 setIterval 正在执行函数时,它在未调用函数时打印 undefined(同时)

    I think you should use useEffect and provide a blank dependency array for calling initializeContext 
    
    import { useState ,useEffect} from "react";
    import "./styles.css";
    
    export default function App() {
      console.log("NEW RENDER");
      
      const [watcherId, setWatcherId] = useState();
      const [updatingInterval, setUpdatingInterval] = useState();
      //test is a dummy variable to manually create re-rendering and check if new intervals get created
      const [test, setTest] = useState(1);
    
      async function initializeContext() {
        console.log("current value of updatingInterval: ", updatingInterval);
        if (updatingInterval === undefined) {
          console.log("new interval created");
          setUpdatingInterval(setInterval(updateValues, 1000));
        }
        console.log("current value of watcherId: ", watcherId);
        if (watcherId === undefined) {
          console.log("new geoWatcher created");
          setWatcherId(navigator.geolocation.watchPosition(success, error, {
            maximumAge: 500,
            enableHighAccuracy: true
          }));
        }
      }
    
      function success() {
        console.log("executed!");
      }
    
      function error(error) {
        console.error("error: ", error);
      }
    
      function buttonFunction() {
        setTest((prevText) => {
          return prevText + 1;
        });
      }
    
      function updateValues() {
        console.log("Interval executed");
      }
     useEffect(()=>{
      initializeContext();
    },[])
      return (
        <div className="App">
          <h1>{test}</h1>
          <button onClick={buttonFunction}>Switch</button>
        </div>
      );
    }

//If any issues still Please ask.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React Native:为什么有时未定义 useState 钩子? - React Native: why are useState hooks sometimes undefined? 我不知道:x 未定义 - I have no idea: x is undefined 为什么在 React 中尝试使用 useState 挂钩时出现“未定义”? - Why am I getting 'undefined' when trying to use the useState hook in React? 为什么 useState 返回未定义? - Why is useState returning undefined? 在javascript中获取未被引用的引用错误,该错误被认为是未定义的变量。我不知道为什么 - Getting an uncaught reference error in javascript on a variable that is said to be undefined. I have no idea why React - useState - 为什么 setTimeout 函数没有最新的状态值? - React - useState - why setTimeout function does not have latest state value? 为什么 useState 有这种行为,以及如何在我设置值的同一函数中获取更新值? 在反应原生 - why useState have this behavier and how I can get updated value in in same function where I set value? In react-native 不能在反应中使用 useState 或 useEffect 我还有什么? - Cannot use useState or useEffect in react what else do I have? 我不知道为什么 state 会这样更新? - I have no idea why state updated like this? 为什么我必须将 function 传递给 React 中“useState”的设置器 function? - Why must I pass a function to the setter function of 'useState' in React?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM