使用 React 16.8.6(在以前的版本 16.8.3 上很好),当我尝试防止获取请求上的无限循环时出现此错误:
./src/components/BusinessesList.js
Line 51: React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array react-hooks/exhaustive-deps
我一直无法找到停止无限循环的解决方案。我想远离使用 useReducer()
。我确实发现了这个讨论[ESLint] Feedback for 'exhaustive-deps' lint rule #14920,其中一个可能的解决方案是You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.
我对我正在做的事情没有信心,所以我还没有尝试实施它。
我有这个当前设置,React hook useEffect runs continuously forever/infinite loop,唯一的评论是关于我不熟悉的 useCallback()
。
我目前如何使用 useEffect()
(我只想在开始时运行一次,类似于 componentDidMount()
):
useEffect(() => {
fetchBusinesses();
}, []);
const fetchBusinesses = () => {
return fetch("theURL", {method: "GET"}
)
.then(res => normalizeResponseErrors(res))
.then(res => {
return res.json();
})
.then(rcvdBusinesses => {
// some stuff
})
.catch(err => {
// some error handling
});
};
如果您没有在效果之外的任何地方使用 fetchBusinesses 方法,您可以简单地将其移动到效果中并避免警告
useEffect(() => {
const fetchBusinesses = () => {
return fetch("theURL", {method: "GET"}
)
.then(res => normalizeResponseErrors(res))
.then(res => {
return res.json();
})
.then(rcvdBusinesses => {
// some stuff
})
.catch(err => {
// some error handling
});
};
fetchBusinesses();
}, []);
但是,如果您在渲染之外使用 fetchBusinesses,则必须注意两件事
在挂载期间使用带有封闭闭包的 fetchBusinesses 作为方法时,您没有将其作为方法传递有什么问题吗?你的方法是否依赖于它从封闭闭包中接收到的一些变量?这不是你的情况。在每次渲染时,都会重新创建 fetchBusinesses,因此将其传递给 useEffect 会导致问题。因此,如果要将 fetchBusinesses 传递给依赖数组,首先必须记住它。
总而言之,我想说如果您在 useEffect
之外使用 fetchBusinesses
,则可以使用 // eslint-disable-next-line react-hooks/exhaustive-deps
禁用规则,否则您可以将方法移到 useEffect 内
要禁用规则,您可以这样编写
useEffect(() => {
// other code
...
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
如果您正在创建一个新应用程序或有足够的灵活性,那么状态管理库有非常好的选择。看看反冲。
只是为了完整性:
1.(停止工作)使用函数作为useEffect回调
useEffect(fetchBusinesses, [])
2.在useEffect()中声明函数
useEffect(() => {
function fetchBusinesses() {
...
}
fetchBusinesses()
}, [])
3. 使用 useCallback() 进行记忆
在这种情况下,如果您的函数中有依赖项,则必须将它们包含在 useCallback
依赖项数组中,如果函数的参数发生更改,这将再次触发 useEffect
。此外,它有很多样板......所以只需将函数直接传递给 useEffect
,就像在 1. useEffect(fetchBusinesses, [])
中一样。
const fetchBusinesses = useCallback(() => {
...
}, [])
useEffect(() => {
fetchBusinesses()
}, [fetchBusinesses])
4.函数的默认参数
正如 Behnam Azimi 所建议的
这不是最佳实践,但在某些情况下可能很有用。
useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);
5.创建自定义钩子
创建一个自定义钩子并在您只需要运行一次函数时调用它。它可能更清洁。您还可以在需要时返回回调以重置重新运行“初始化”。
// customHooks.js
const useInit = (callback, ...args) => {
const [mounted, setMounted] = useState(false)
const resetInit = () => setMounted(false)
useEffect(() => {
if(!mounted) {
setMounted(true);
callback(...args);
}
},[mounted, callback]);
return [resetInit]
}
// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
const [resetInit] = useInit(fetchBusiness, arg1, arg2)
useEffect(() => {
resetInit()
}, [requiresRefetch, resetInit]);
6.禁用eslint的警告
禁用警告应该是你最后的手段,但是当你这样做时,最好内联和明确地这样做,因为未来的开发人员可能会感到困惑或在不知道 linting 关闭的情况下创建意外错误
useEffect(() => {
fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
useEffect(fetchBusinesses, [])
将抛出 "TypeError: Effect 函数不能返回除用于清理的函数之外的任何内容。" 因为 fetchBusinesses
返回一个 Promise。
./src/components/BusinessesList.js
Line 51: React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array react-hooks/exhaustive-deps
这不是 JavaScript/React 错误,而是 ESLint (eslint-plugin-react-hooks) 警告。
它告诉您该钩子依赖于函数 fetchBusinesses
,因此您应该将它作为依赖项传递。
useEffect(() => {
fetchBusinesses();
}, [fetchBusinesses]);
如果函数在如下组件中声明,则可能导致在每次渲染时调用该函数:
const Component = () => {
/*...*/
// New function declaration every render
const fetchBusinesses = () => {
fetch('/api/businesses/')
.then(...)
}
useEffect(() => {
fetchBusinesses();
}, [fetchBusinesses]);
/*...*/
}
因为每次函数都会用新的引用重新声明。
做这些事情的正确方法是:
const Component = () => {
/*...*/
// Keep the function reference
const fetchBusinesses = useCallback(() => {
fetch('/api/businesses/')
.then(...)
}, [/* Additional dependencies */])
useEffect(() => {
fetchBusinesses();
}, [fetchBusinesses]);
/*...*/
}
或者只是在 useEffect
中定义函数。
更多:[ESLint] Feedback for 'exhaustive-deps' lint rule #14920
React 也给出了解决方案。他们建议您使用 useCallback
,它将返回您的函数的 memoize 版本:
'fetchBusinesses' 函数使 useEffect Hook(在第 NN 行)的依赖关系在每次渲染时都发生变化。为了解决这个问题,将 'fetchBusinesses' 定义包装到它自己的 useCallback() Hook react-hooks/exhaustive-deps
useCallback
使用简单,因为它与 useEffect
具有相同的签名。不同之处在于 useCallback 返回一个函数。它看起来像这样:
const fetchBusinesses = useCallback( () => {
return fetch("theURL", {method: "GET"}
)
.then(() => { /* Some stuff */ })
.catch(() => { /* Some error handling */ })
}, [/* deps */])
// We have a first effect that uses fetchBusinesses
useEffect(() => {
// Do things and then fetchBusinesses
fetchBusinesses();
}, [fetchBusinesses]);
// We can have many effects that use fetchBusinesses
useEffect(() => {
// Do other things and then fetchBusinesses
fetchBusinesses();
}, [fetchBusinesses]);
useCallBack
钩子解决了我的问题。要查看详细信息,请访问 documentation
这些警告对于查找更新不一致的组件非常有帮助:Is it safe to omit functions from the list of dependencies?。
但是,如果您想在整个项目中删除警告,您可以将其添加到您的 ESLint 配置中:
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/exhaustive-deps": 0
}
}
const [mount, setMount] = useState(false)
const fetchBusinesses = () => {
// Function definition
}
useEffect(() => {
if(!mount) {
setMount(true);
fetchBusinesses();
}
},[fetchBusinesses, mount]);
这个解决方案非常简单,您不需要覆盖 ESLint 警告。只需维护一个标志来检查组件是否已安装。
只需为下一行禁用 ESLint;
useEffect(() => {
fetchBusinesses();
// eslint-disable-next-line
}, []);
通过这种方式,您可以像使用组件一样使用它(调用一次)。
更新
或者
const fetchBusinesses = useCallback(() => {
// Your logic in here
}, [someDeps])
useEffect(() => {
fetchBusinesses();
// No need to skip the ESLint warning
}, [fetchBusinesses]);
每次 someDeps 更改时都会调用 fetchBusinesses。
[fetchBusinesses]
将自动删除警告并为我解决了这个问题。
useEffect
中检查状态是否为空的函数之前编写一个 if 条件。
这篇文章是关于使用钩子获取数据的很好的入门文章:https://www.robinwieruch.de/react-hooks-fetch-data/
本质上,在 useEffect
中包含 fetch 函数定义:
useEffect(() => {
const fetchBusinesses = () => {
return fetch("theUrl"...
// ...your fetch implementation
);
}
fetchBusinesses();
}, []);
实际上,当您使用钩子进行开发时,警告非常有用。但在某些情况下,它会刺伤你。特别是当您不需要监听依赖项变化时。
如果您不想将 fetchBusinesses
放在钩子的依赖项中,您可以简单地将它作为参数传递给钩子的回调并将主 fetchBusinesses
设置为它的默认值,如下所示:
useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);
这不是最佳实践,但在某些情况下可能很有用。
此外,作为 Shubham wrote,您可以添加以下代码来告诉 ESLint 忽略对您的钩子的检查。
// eslint-disable-next-line react-hooks/exhaustive-deps
你试试这样:
const fetchBusinesses = () => {
return fetch("theURL", {method: "GET"})
.then(res => normalizeResponseErrors(res))
.then(res => {
return res.json();
})
.then(rcvdBusinesses => {
// Some stuff
})
.catch(err => {
// Some error handling
});
};
和
useEffect(() => {
fetchBusinesses();
});
它对你有用。
但我的建议是尝试这种方式,它也适用于你。这比以前的方法好。我这样使用它:
useEffect(() => {
const fetchBusinesses = () => {
return fetch("theURL", {method: "GET"})
.then(res => normalizeResponseErrors(res))
.then(res => {
return res.json();
})
.then(rcvdBusinesses => {
// Some stuff
})
.catch(err => {
// Some error handling
});
};
fetchBusinesses();
}, []);
如果您基于特定 id 获取数据,则添加回调 useEffect [id]
。然后它无法向您显示警告 React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array
您可以删除第二个参数类型数组 []
,但每次更新时也会调用 fetchBusinesses()
。如果您愿意,可以将 IF
语句添加到 fetchBusinesses()
实现中。
React.useEffect(() => {
fetchBusinesses();
});
另一种是在您的组件之外实现 fetchBusinesses()
函数。只是不要忘记将任何依赖参数传递给您的 fetchBusinesses(dependency)
调用(如果有)。
function fetchBusinesses (fetch) {
return fetch("theURL", { method: "GET" })
.then(res => normalizeResponseErrors(res))
.then(res => res.json())
.then(rcvdBusinesses => {
// some stuff
})
.catch(err => {
// some error handling
});
}
function YourComponent (props) {
const { fetch } = props;
React.useEffect(() => {
fetchBusinesses(fetch);
}, [fetch]);
// ...
}
好吧,如果你想以不同的方式看待这个问题,你只需要知道 React 有哪些非 exhaustive-deps
选项。您不应该在效果内使用闭包函数的原因之一是在每次渲染时,它将再次被重新创建/销毁。
因此,钩子中有多个 React 方法被认为是稳定且未用尽的,您不必应用到 useEffect
依赖项,反过来也不会破坏 react-hooks/exhaustive-deps
的规则参与。例如,useReducer
或 useState
的第二个返回变量是一个函数。
const [,dispatch] = useReducer(reducer, {});
useEffect(() => {
dispatch(); // Non-exhausted - ESLint won't nag about this
}, []);
因此,反过来,您可以让所有外部依赖项与您的 reducer 函数中的当前依赖项共存。
const [,dispatch] = useReducer((current, update) => {
const { foobar } = update;
// Logic
return { ...current, ...update };
}), {});
const [foobar, setFoobar] = useState(false);
useEffect(() => {
dispatch({ foobar }); // non-exhausted `dispatch` function
}, [foobar]);
就我而言,它对我的局部变量 organization
有此警告,当我将 organization
放入依赖项数组时,useEffect
将无限获取。因此,如果您遇到像我这样的问题,请将 useEffect
与依赖数组一起使用并拆分:
因为如果您有多个修改状态的 API 调用,它会多次调用 useEffect
。
从:
const { organization } = useSelector(withOrganization)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getOrganization({}))
dispatch(getSettings({}))
dispatch(getMembers({}))
}, [dispatch, organization])
至:
const { organization } = useSelector(withOrganization)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getOrganization({}))
dispatch(getSettings({}))
}, [dispatch, organization])
useEffect(() => {
dispatch(getMembers({}))
}, [dispatch])
如果您在 useEffect
中使用的变量在组件内定义或作为道具传递给组件,则会出现此警告。由于您在同一组件中定义了 fetchBusinesses()
,因此您必须将其传递给依赖项数组。
但是,如果您导入 fetchBusinesses()
,然后在 useEffect
中使用它,则不需要将它添加到依赖项数组中。这就是我们实际设置 Redux 应用程序的方式:我们总是导入我们的动作创建者并在 useEffect
中运行它,而不将其添加到依赖项数组中。
useMemo
也是如此。
connect(mapStateToProps, {fetchBusinesses})(Component)
您可以通过传递对它的引用来摆脱这个 Es-lint 警告:
下面提到的示例,但是您可以在此链接上观看解决方案:https://www.youtube.com/watch?v=r4A46oBIwZk&t=8s
警告:第 13:8 行:React Hook React.useEffect 缺少依赖项:'history' 和 'currentUser?.role'。要么包含它们,要么删除依赖数组 react-hooks/exhaustive-deps
React.useEffect(() => {
if (currentUser?.role !== "Student") {
return history.push("/")
}
}, [])
解决方案:第 1 步:将业务逻辑移至单独的 const。
现在警告是:React Hook React.useEffect 缺少依赖项:'roleChecking'。
const roleChecking = () =>{
if (currentUser?.role !== "Student") {
return history.push("/")
}
}
React.useEffect(() => {
roleChecking()
}, [])
最后一步是创建对该函数的引用:
const roleRef = React.useRef();
const roleChecking = () => {
if (currentUser?.role !== "Student") {
return history.push("/");
}
};
roleRef.current = roleChecking;
React.useEffect(() => {
return roleRef.current();
}, [currentUser?.role]);
似乎在组件中声明了 fetchBusinesses
函数。这意味着在每次渲染中它都声明了触发钩子的新函数。
有 2 种方法可以解决此问题。
将 fetchBusinesses 函数声明移出组件。使用 useCallback 钩子包装 fetchBusinesses 函数。
第一个选项更可取。
这不是特定于问题用例的答案,而是更一般的情况,并涵盖了 useEffect 或提取和导入不工作的情况。 useRef 场景:
有时情况是 useEffect 应该有一个空数组,你仍然想在 state 的 useEffect 部分中使用,但你仍然不想将它们作为依赖项注入,你也可以尝试 useCallback 现在反应是抱怨依赖项useCallback 和你卡住了。在这种情况下,您可以使用 useRef。例如:
const locationRef = useRef(location);
useEffect(()=>{
const qs = locationRef.current.search
...
},[])
使用此技术时应小心,并注意 useRef 不会激活渲染进程。
只需将函数作为参数传递到 useEffect 数组中...
useEffect(() => {
functionName()
}, [functionName])
使用 UseEffect fetchBusinesses
调用函数在 useEffect()
中声明,方法是在调用函数名称之后声明一个 const 变量,
useEffect(()=>{
const fetchBusinesses=()=>{
console.log(useeffect fetchbussinesses functions)
}
fetchBusinesses();
},[declare the variable used in useeffect hooks ])
您正在使用 useEffect
,当您这样做时,您经常希望使用一些变量,这些变量用作组件内的道具或状态。
eslint 中内置了一条规则,希望您引用 useEffect
依赖数组内的任何不同的 prop 或状态片段。这是控制 useEffect
何时执行的数组。该规则希望看到它列在该数组中,该数组决定何时重新运行 useEffect
函数。
因此,您需要添加 [fetchBusinesses]
并且警告应该消失。
现在,为什么这条规则要我们把它放在那里?
在某些情况下,使用 useEffect
而没有正确列出数组中的所有状态和道具可能会导致奇怪且难以调试的问题。
所以这条规则是为了帮助避免那些难以理解的问题,这些问题可能会出现 useEffect
。
现在,任意添加到该数组也可能导致错误。因此,无论哪种方式,您都遇到了必须解决的错误。根据您的评论,似乎可以为您解决问题,但我想进一步调查,看看您是否有机会在 Chrome 的 Network 标签中收到第二个 GET
请求,然后添加useEffect
数组的 fetchBusinesses
函数。
搜索关键字以了解有关每个警告的更多信息。要忽略,请将 // eslint-disable-next-line 添加到前一行。
例如: useEffect 中使用的函数导致警告
useEffect(() => { handleConnectWallet(); }, []);
要忽略警告,我们只需在警告行之前添加“// eslint-disable-next-line”,即
useEffect(() => { handleConnectWallet(); // eslint-disable-next-line }, []);
useCallback()
。例如:const fetchBusinesses= useCallback(() => { ... }, [...])
和useEffect()
看起来像这样:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
// eslint-disable-next-line react-hooks/exhaustive-deps
向 linter 解释您的代码是正确的就像 hack。我希望他们会找到另一种解决方案,使 linter 足够聪明,以检测何时不是强制性的参数