Javascript setInterval 未检测到全局变量更改 (React Native)
Javascript setInterval not detecting global variable change (React Native)
我正在开发一个用于与 BLE 设备通信的 React Native 应用程序。为了跟踪连接是否仍然建立,我在我的组件中使用了一个状态。
const [isConnected, setIsConnected] = useState(props.route.params.isConnected || false);
在我的 useEffect
挂钩中,我开始一个间隔来重复读取我的 BLE 设备的状态,如下所示:
useEffect(() => {
AppState.addEventListener('change', onAppStateChange);
updateStatusRepeatedly.current = setInterval(readStatus, 5000);
return () => {
AppState.removeEventListener('change', onAppStateChange);
clearInterval(updateStatusRepeatedly.current);
};
}, []);
readStatus
函数每 5 秒调用一次,按预期工作。但是不应该执行的内容。所以我把它包装在一个基于 isConnected
变量的条件语句中。
const readStatus = () => {
if (isConnected) {
readAddedCharge();
readPowerMeasurement();
}
};
内容每次都会执行。即使 isConnected
变量设置为 false。我不明白为什么,因为其他功能和组件正确考虑了 isConnected
。
知道为什么其他函数和组件会注册 isConnected
的更改,但 readStatus
作为计时器调用的函数却不会吗?
看来这是一个React的函数组件。让我们做一个最小的例子:
function MyComp() {
const [foo, setFoo] = useState(0);
useEffect(() => {
setInterval(() => {
console.log(foo); // this will always show 0
setFoo(foo + 1);
}, 5000);
}, []);
}
是因为在函数的范围内,foo
是一个永远不变的const
。 “更改”foo 意味着 运行 函数再次具有新值 foo
,您无论如何都不要使用它,因为 useEffect(..., [])
仅在第一次渲染时调用。
可能的解决方案:
- 清除并re-create每个组件渲染的间隔
- 使用
useRef
存储值,以便第一次渲染的 readStatus
始终引用 up-to-date 值
我遇到了同样的问题,但找到了解决方案。我们创建了一个执行间隔的组件,它在事物的反应端调用一个外部函数。例如,请参见下面的代码。
const intervalExample = () => {
const [timeslots, setTimeslots] = useState([]);
const loopingFunction = () => {
if (timeslots.length > 0) {
timeslots.map((val) => {
if (val['purchasing'] == true) {
doStuff();
}
});
}
}
const doStuff = () = >{
}
return <div><IntervalRunner onInterval={loopingFunction} milliseconds="5000" /></div>
}
const IntervalRunner = (props) => {
const [increments, setIncrements] = useState(0);
const intervalCall = () => {
setIncrements(increments+1);
}
useEffect(()=>{
props.onInterval();
setTimeout(intervalCall, props.milliseconds)
}
, [increments])
return '';
}
以上组件是相当基础的,可能需要改进。在最后几分钟,我意识到我们需要一个 on/off 开关。模组如下。
export const IntervalRunner = (props) => {
const [increments, setIncrements] = useState(0);
const intervalCall = () => {
if(props.active){
setIncrements(increments+1);
}
}
useEffect(()=>{
if(props.active){
props.onInterval();
setTimeout(intervalCall, props.milliseconds);
}
}
, [increments, props.active])
return '';
}
用于:
return <div><IntervalRunner onInterval={loopingFunction} milliseconds="5000" active={activeVar} /></div>
我正在开发一个用于与 BLE 设备通信的 React Native 应用程序。为了跟踪连接是否仍然建立,我在我的组件中使用了一个状态。
const [isConnected, setIsConnected] = useState(props.route.params.isConnected || false);
在我的 useEffect
挂钩中,我开始一个间隔来重复读取我的 BLE 设备的状态,如下所示:
useEffect(() => {
AppState.addEventListener('change', onAppStateChange);
updateStatusRepeatedly.current = setInterval(readStatus, 5000);
return () => {
AppState.removeEventListener('change', onAppStateChange);
clearInterval(updateStatusRepeatedly.current);
};
}, []);
readStatus
函数每 5 秒调用一次,按预期工作。但是不应该执行的内容。所以我把它包装在一个基于 isConnected
变量的条件语句中。
const readStatus = () => {
if (isConnected) {
readAddedCharge();
readPowerMeasurement();
}
};
内容每次都会执行。即使 isConnected
变量设置为 false。我不明白为什么,因为其他功能和组件正确考虑了 isConnected
。
知道为什么其他函数和组件会注册 isConnected
的更改,但 readStatus
作为计时器调用的函数却不会吗?
看来这是一个React的函数组件。让我们做一个最小的例子:
function MyComp() {
const [foo, setFoo] = useState(0);
useEffect(() => {
setInterval(() => {
console.log(foo); // this will always show 0
setFoo(foo + 1);
}, 5000);
}, []);
}
是因为在函数的范围内,foo
是一个永远不变的const
。 “更改”foo 意味着 运行 函数再次具有新值 foo
,您无论如何都不要使用它,因为 useEffect(..., [])
仅在第一次渲染时调用。
可能的解决方案:
- 清除并re-create每个组件渲染的间隔
- 使用
useRef
存储值,以便第一次渲染的readStatus
始终引用 up-to-date 值
我遇到了同样的问题,但找到了解决方案。我们创建了一个执行间隔的组件,它在事物的反应端调用一个外部函数。例如,请参见下面的代码。
const intervalExample = () => {
const [timeslots, setTimeslots] = useState([]);
const loopingFunction = () => {
if (timeslots.length > 0) {
timeslots.map((val) => {
if (val['purchasing'] == true) {
doStuff();
}
});
}
}
const doStuff = () = >{
}
return <div><IntervalRunner onInterval={loopingFunction} milliseconds="5000" /></div>
}
const IntervalRunner = (props) => {
const [increments, setIncrements] = useState(0);
const intervalCall = () => {
setIncrements(increments+1);
}
useEffect(()=>{
props.onInterval();
setTimeout(intervalCall, props.milliseconds)
}
, [increments])
return '';
}
以上组件是相当基础的,可能需要改进。在最后几分钟,我意识到我们需要一个 on/off 开关。模组如下。
export const IntervalRunner = (props) => {
const [increments, setIncrements] = useState(0);
const intervalCall = () => {
if(props.active){
setIncrements(increments+1);
}
}
useEffect(()=>{
if(props.active){
props.onInterval();
setTimeout(intervalCall, props.milliseconds);
}
}
, [increments, props.active])
return '';
}
用于:
return <div><IntervalRunner onInterval={loopingFunction} milliseconds="5000" active={activeVar} /></div>