如何防止 useEffect 中的函数被多次触发?
how do you prevent functions inside useEffect from being triggered more than once?
有一个 ESLint 规则 exhaustive-deps
检查你的 Hook 的依赖数组(例如:useEffect
)。我的问题在于由于现在正在跟踪函数之类的事情而更频繁地触发 useEffect 的内容的影响。
当 api 调用在效果内时,这可能会产生负面影响,因为您只希望 API 被调用一次。
我可以进一步分解 Effect,也许它对 dep 数组有帮助...也许不会...
如何防止内容被触发超过其需要?
对我来说,为了防止对 API 服务的额外调用,我要么需要忽略 lint 规则(不!),要么对我的代码进行更多检查,以及关于状态的本地状态:
防止会话和 api 调用被多次触发的示例:
useEffect(() => {
if (!saveValues) { // is this an anti-pattern?
return;
}
if (!sessionId && !wasSaving && savingForm) {
initSession().then(() => { // 1st call to session service to get Id
setSendRequest(true); // set local state that it's safe to send API request because I have my session ready
});
} else if (sendRequest && sessionId) {
setSendRequest(false); // immediately set local state back to false so the previous block doesnt fire.
try {
const fields = userFields
.filter(field => !isEmpty(saveValues[field.name]))
.map(field => {
name: field.name,
value,
};
);
insertUser({ sessionId, fields, config, newRoles }) // here's my API call now
.then(() => saveSession(sessionId)) // 2nd call to session service req'd
} catch (err) {
const message = err.message.split('desc = ')[1];
setSaveRequestError(message);
setSavingForm(false);
setSaveModalOpen(false);
quitSession(sessionId);
}
}
return () => {
if (sessionId) {
quitSession(sessionId);
}
};
}, [
initSession,
insertUser,
quitSession,
roles.rolesList,
saveSession,
saveValues,
savingForm,
sendRequest,
sessionId,
userFields,
wasSaving,
]);
作为旁注,有趣的是,状态的挂钩,如 setSaveRequestError、setSavingForm 没有出现在 dep 数组中...
每当传递给它的依赖项发生变化时,useEffect 就会被调用,并在其中运行整个代码。
注意:useEffect(() => {}, [dependency1, dependency2 ...])
如果您希望仅在组件初始加载时调用 useEffect 中的代码,请将空数组作为依赖项传递。
由于 React 文档 here and here 最好将每个条件放在一个单独的 useEffect
而不是一个复杂的条件中。每个 useEffect
都有自己的依赖数组并做特定的工作。
如果你只是想避免再次执行它(并且你不想传递一个空的 dep 数组)useRef 是你的朋友,因为它不会重新渲染组件(不是状态):
let hasBeenExecuted = useRef(false);
//In effect
if(!hasBeenExecuted.current) {
// Do stuff
hasBeenExecuted.current = true
}
对不起,我迟到了。这个仓库就是为了解决这个问题。
useWatch(() => {
// your logic
}, [your dependency])
巧妙而完美地解决了这个问题。
有一个 ESLint 规则 exhaustive-deps
检查你的 Hook 的依赖数组(例如:useEffect
)。我的问题在于由于现在正在跟踪函数之类的事情而更频繁地触发 useEffect 的内容的影响。
当 api 调用在效果内时,这可能会产生负面影响,因为您只希望 API 被调用一次。
我可以进一步分解 Effect,也许它对 dep 数组有帮助...也许不会...
如何防止内容被触发超过其需要? 对我来说,为了防止对 API 服务的额外调用,我要么需要忽略 lint 规则(不!),要么对我的代码进行更多检查,以及关于状态的本地状态:
防止会话和 api 调用被多次触发的示例:
useEffect(() => {
if (!saveValues) { // is this an anti-pattern?
return;
}
if (!sessionId && !wasSaving && savingForm) {
initSession().then(() => { // 1st call to session service to get Id
setSendRequest(true); // set local state that it's safe to send API request because I have my session ready
});
} else if (sendRequest && sessionId) {
setSendRequest(false); // immediately set local state back to false so the previous block doesnt fire.
try {
const fields = userFields
.filter(field => !isEmpty(saveValues[field.name]))
.map(field => {
name: field.name,
value,
};
);
insertUser({ sessionId, fields, config, newRoles }) // here's my API call now
.then(() => saveSession(sessionId)) // 2nd call to session service req'd
} catch (err) {
const message = err.message.split('desc = ')[1];
setSaveRequestError(message);
setSavingForm(false);
setSaveModalOpen(false);
quitSession(sessionId);
}
}
return () => {
if (sessionId) {
quitSession(sessionId);
}
};
}, [
initSession,
insertUser,
quitSession,
roles.rolesList,
saveSession,
saveValues,
savingForm,
sendRequest,
sessionId,
userFields,
wasSaving,
]);
作为旁注,有趣的是,状态的挂钩,如 setSaveRequestError、setSavingForm 没有出现在 dep 数组中...
每当传递给它的依赖项发生变化时,useEffect 就会被调用,并在其中运行整个代码。
注意:useEffect(() => {}, [dependency1, dependency2 ...])
如果您希望仅在组件初始加载时调用 useEffect 中的代码,请将空数组作为依赖项传递。
由于 React 文档 here and here 最好将每个条件放在一个单独的 useEffect
而不是一个复杂的条件中。每个 useEffect
都有自己的依赖数组并做特定的工作。
如果你只是想避免再次执行它(并且你不想传递一个空的 dep 数组)useRef 是你的朋友,因为它不会重新渲染组件(不是状态):
let hasBeenExecuted = useRef(false);
//In effect
if(!hasBeenExecuted.current) {
// Do stuff
hasBeenExecuted.current = true
}
对不起,我迟到了。这个仓库就是为了解决这个问题。
useWatch(() => {
// your logic
}, [your dependency])
巧妙而完美地解决了这个问题。