[英]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.