gpt4 book ai didi

javascript - React.StrictMode : SetState function in useEffect is run multiple times when effect is run once

转载 作者:行者123 更新时间:2023-12-01 00:07:05 25 4
gpt4 key购买 nike

当oldRunIn为undefined时,下面代码的输出与触发效果时的预期一致:

Effect is running

setState is running

但是,下次 useEffect 运行时定义了状态变量 runInArrow(在 setState 函数中称为 oldRunInArrow),输出为:

Effect is running

setState is running

setState is running

setState is running

怎么可能effect只运行了一次而setState运行了3次?

const [runInArrow, setRunInArrow] = useState<mapkit.PolylineOverlay[] | undefined>(undefined);


useEffect(() => {
const trueRunIn = settings.magneticRunIn + magVar;

const boundingRegion = region.toBoundingRegion();
const style = new mapkit.Style({
lineWidth: 5,
lineJoin: 'round',
strokeColor: settings.runInColor,
});
const newRunInArrow = createArrow(boundingRegion, trueRunIn, style);
console.log('Effect is running');

setRunInArrow(oldRunIn => {

// This runs too many times when oldRunIn aka runInArrow is defined

console.log('setState is running');
if (oldRunIn) map.removeOverlays(oldRunIn);
return newRunInArrow;
});
map.addOverlays(newRunInArrow);
}, [magVar, map, mapkit, region, settings.magneticRunIn, settings.runInColor, settings.showRunIn]);

要了解为什么我使用函数来设置状态,请参阅此 post

<小时/>

编辑:

这很奇怪。如果我删除 if (oldRunIn) map.removeOverlays(oldRunIn); 它会按预期工作。但是,如果我将其更改为 if (oldRunIn) console.log('oldRunIn is Defined'); ,它仍然会运行多次。我彻底糊涂了。

if (oldRunIn) map.addAnnotations([]); 不会运行多次。

if (oldRunIn) console.log('test'); 确实运行了多次。

它总是运行多次(每个使用效果 6 次):

  setRunInArrow(oldRunIn => {
console.log('setState is running');
console.log('test');
return newRunInArrow;
});

这不会运行多次:

  setRunInArrow(oldRunIn => {
console.log('setState is running');
return newRunInArrow;
});
<小时/>

编辑2:

可重现的示例。单击按钮。您甚至不需要 if (oldState) console.log('Old state'); 您只需放入第二个 console.log 即可更改行为。

import { FunctionComponent, useState, useEffect } from 'react';

export const Test: FunctionComponent<> = function Test() {
const [state, setState] = useState<number | undefined>(undefined);
const [triggerEffect, setTriggerEffect] = useState(0);

useEffect(() => {
console.log('effect is running');
setState(oldState => {
console.log('setState is runnning');
if (oldState) console.log('Old state');
return 1;
});
}, [triggerEffect]);

return <>
<button onClick={() => setTriggerEffect(triggerEffect + 1)} type="button">Trigger Effect</button>
</>;
};
<小时/>

编辑3:

我通过将此代码放入另一个 nextJS 项目中来重现此内容。我猜这与 nextJS 有关。

<小时/>

编辑4:

这不是 NextJS。它将它包装在 React.StrictMode 中。这是sandbox 。为什么?

<小时/>

编辑5:

正如答案所指出的,问题是由于 StrictMode 故意运行代码两次造成的。它不应该根据文档运行 useReducer 两次(这是我的其他问题中的“react-hooks/exhaustive-deps”的问题)。这是一个 UseReducer 演示,尝试使用和不使用 StrictMode。它还运行两次。看来它也需要纯净:

CodeSandbox

import { useState, useEffect, useReducer } from 'react';

function reducer(state, data) {
console.log('Reducer is running');
if (state) console.log(state);
return state + 1;
}

export const Test = function Test() {
const [state, dispatch] = useReducer(reducer, 1);
const [triggerEffect, setTriggerEffect] = useState(0);

useEffect(() => {
dispatch({});
}, [triggerEffect]);

return (
<>
<button onClick={() => setTriggerEffect(triggerEffect + 1)} type="button">
Trigger Effect
</button>
</>
);
};

const Home = () => (
<React.StrictMode>
<Test></Test>
</React.StrictMode>
);

export default Home;

最佳答案

正如您所发现的,当您使用 React 严格模式时就会发生这种情况,并且这是故意的。

this article 中所述:

It runs code TWICE

Another thing that React Strict Mode does is run certain callbacks/methods twice (in DEV mode ONLY). You read that right! The following callbacks/methods will be run twice in Strict Mode (in DEV mode ONLY):

  • Class component constructor method
  • The render method (includes function components)
  • setState updater functions (the first argument)
  • The static getDerivedStateFromProps lifecycle
  • The React.useState state initializer callback function
  • The React.useMemo callback

Checkout this codesandbox which logs to the console in hook callbacks and class methods to show you that certain things happen twice.

React 这样做是因为它无法可靠地警告您在这些方法中产生的副作用。但如果这些方法是幂等的,那么多次调用它们不会造成任何麻烦。如果它们不是幂等的,那么您应该注意到一些有趣的事情,您应该能够注意到并修复这些事情。

Note that useEffect and useLayoutEffect callbacks are not called twice even in dev mode + strict mode because the entire point of those callbacks is to perform side-effects.

Note that I also observed that the reducer you pass to React.useReducer is not called twice in dev mode. I'm not sure why this is because I feel like that could also benefit from this kind of warning.

如上所示,包含此行为是为了帮助您查找代码中的错误。由于更新程序函数应该是幂等且纯粹的,因此运行它们两次而不是一次应该不会对应用程序的功能产生影响。如果确实如此,那么这就是您的代码中的错误。

听起来像 map.removeOverlays()您调用的方法是一个有效的函数,因此不应在状态更新器函数中调用它。根据 your other question 的答案,我可以明白为什么您要以这种方式实现它,但我认为使用 chitova263 的答案可以解决这个问题。

关于javascript - React.StrictMode : SetState function in useEffect is run multiple times when effect is run once,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60305074/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com