我有一个在useEffect
中使用并单击事件的函数,然后引发警告“ React Hook useEffect缺少依赖项”,我应该如何删除警告?
// location is react router location
const Component = ({ location }) => {
const [data, setData] = useState(null);
const fetchData = () => {
const { id } = parseLocation(location);
fetchDataFromServer(id).then(data => setData(data));
}
useEffect(() => {
fetchData();
}, [location]);
return (
<div>
{data}
<button onClick={fetchData)}>reload</button>
</div>
);
}
然后我尝试此操作,但警告仍然存在
// location is react router location
const Component = ({ location }) => {
const [data, setData] = useState(null);
const fetchData = (l) => {
// l is location
const { id } = parseLocation(l);
fetchDataFromServer(id).then(data => setData(data));
}
useEffect(() => {
fetchData(location);
}, [location]);
return (
<div>
{data}
<button onClick={() => fetchData(location)}>reload</button>
</div>
);
}
答案 0 :(得分:1)
exhaustive-deps
规则的重点是防止挂钩读取陈旧的道具或状态。问题在于,由于fetchData
是在组件内定义的,就短绒而言,它可能正在访问陈旧的props
或状态(通过闭包)。
一种解决方案是将fetchData
从组件中拉出,并将其所需的所有内容传递给它:(已经通过了位置):
const fetchData = (l, setData) => {
// l is location
const { id } = parseLocation(l);
fetchDataFromServer(id).then(data => setData(data));
}
const Component = ({ location }) => {
const [data, setData] = useState(null);
useEffect(() => {
fetchData(location, setData);
}, [location]);
return (
<div>
{data}
<button onClick={() => fetchData(location, setData)}>reload</button>
</div>
);
}
由于fetchData
不在组件外部定义,因此linter知道它不会访问状态或道具,因此对于陈旧数据而言这不是问题。
不过,要清楚一点,从运行时的角度来看,您的原始解决方案是正确,因为fetchData
不读取状态或道具-但是linter不知道。
您可以简单地禁用linter,但是以后以这种方式(如果fetchData
曾经被修改过)不小心引入错误很容易。最好使用linter规则来验证正确性,即使这意味着对代码进行一些轻微的重组。
利用闭包而不是将位置传递到fetchData
的替代解决方案:
const Component = ({ location }) => {
const [data, setData] = useState(null);
const fetchData = useCallback(() => {
// Uses location from closure
const { id } = parseLocation(location);
fetchDataFromServer(id).then(data => setData(data));
// Ensure that location isn't stale
}, [location]);
useEffect(() => {
fetchData();
// Ensure that fetchData isn't stale
}, [fetchData]);
return (
<div>
{data}
<button onClick={fetchData}>reload</button>
</div>
);
}
这种方法可以避免每次调用时将location
传递到fetchData
中。但是,使用这种方法时,务必确保避免过时的状态和道具。
如果您省略了[fetchData]
的{{1}}深度,则效果只会运行一次,并且useEffect
更改后将不会获取新数据。
但是,如果您在location
的部门中有fetchData
,但没有将useEffect
包裹在fetchData
中,则useCallback
函数是每个渲染,这将导致fetchData
运行每个渲染(这很糟糕)。
通过包装在useEffect
中,useCallback
函数只是fetchData
发生变化时的一个新函数,这会导致location
在适当的位置运行。