如何修复使用useEffects React Hook时缺少的依赖警告

使用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]对“穷举-deps”lint规则#14920的反馈,其中一个可能的解决方案是You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.我对我正在做的事情没有信心,所以我还没有尝试实现它。

我有这个当前的设置,React钩子useEffects永远连续运行/无限循环,唯一的评论是关于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
});
};
869935 次浏览

本文是使用钩子获取数据的良好入门:https://www.robinwieruch.de/react-hooks-fetch-data/

本质上,在useEffect中包含提取函数定义:

useEffect(() => {
const fetchBusinesses = () => {
return fetch("theUrl"...
// ...your fetch implementation
);
}


fetchBusinesses();
}, []);
./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-hoks)警告。

它告诉您钩子依赖于函数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]对“穷举-deps”lint规则#14920的反馈

如果您没有在效果之外的任何地方使用readchBusin的方法,您可以简单地将其移动到效果中并避免警告

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();
}, []);

然而,如果你在渲染之外使用了get业务,你必须注意两件事

  1. 是否有任何问题与你没有传递fetchBusinesses作为一个方法,当它在挂载期间使用其封闭闭包?
  2. 您的方法是否依赖于它从其封闭闭包接收到的一些变量?对您来说不是这样。
  3. 在每一次渲染中,readchBusin的都会被重新创建,因此将其传递给useEffects会导致问题。所以首先,如果你要将它传递给依赖数组,你必须记住readchBusiness。

综上所述,我想说,如果你在useEffect之外使用fetchBusinesses,你可以使用// eslint-disable-next-line react-hooks/exhaustive-deps禁用规则,否则你可以将方法移动到useEffects中

要禁用规则,你会写得像

useEffect(() => {
// other code
...


// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

只需为下一行禁用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]);

每次一些依赖更改时都会调用FedchBusines。

您可以删除第二个参数类型数组[],但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也提供了解决方案。他们建议您使用useCallback,这将返回您的函数的回忆版本:

'readchBusin的'函数使useEffects Hook(在NN行)的依赖关系在每次渲染时都发生变化。要解决这个问题,请将'readchBusin的定义包装到它自己的useCallback()Hook react-hoks/Extive-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]);

如果您正在创建一个新应用程序或具有足够的灵活性,状态管理库有非常好的选择。查看后坐力。

为了完整性:

<打击>1.(停止工作)使用函数作为useEffect回调

useEffect(fetchBusinesses, [])

2.在useEffect()内部声明函数

useEffect(() => {
function fetchBusinesses() {
...
}
fetchBusinesses()
}, [])

3.用useCallback()回味

在这种情况下,如果你的函数中有依赖项,你必须将它们包含在useCallback依赖数组中,如果函数的参数发生变化,这将再次触发useEffect。此外,它有很多样板…所以只需像1. useEffect(fetchBusinesses, [])一样将函数直接传递给useEffect

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的警告

禁用警告应该是您的最后手段,但当您这样做时,最好这样做内联和显式,因为未来的开发人员可能会感到困惑或在不知道lint已关闭的情况下创建意外的错误

useEffect(() => {
fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

这些警告对于查找不一致更新的组件非常有帮助:从依赖列表中省略函数是否安全?

但是,如果您想在整个项目中删除警告,您可以将其添加到ESLint配置中:

  {
"plugins": ["react-hooks"],
"rules": {
"react-hooks/exhaustive-deps": 0
}
}

实际上,当您使用钩子开发时,警告非常有用。但在某些情况下,它会刺痛您。尤其是当您不需要侦听依赖项更改时。

如果您不想将fetchBusinesses放在钩子的依赖项中,您可以简单地将其作为参数传递给钩子的回调,并将主fetchBusinesses设置为它的默认值,如下所示:

useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);

这不是最佳做法,但在某些情况下可能有用。

此外,作为Shubham写道,您可以添加以下代码来告诉ESLint忽略对钩子的检查。

// eslint-disable-next-line react-hooks/exhaustive-deps
const [mount, setMount] = useState(false)
const fetchBusinesses = () => {
// Function definition
}
useEffect(() => {
if(!mount) {
setMount(true);
fetchBusinesses();
}
},[fetchBusinesses, mount]);

这个解决方案非常简单,您不需要覆盖ESLint警告。只需维护一个标志来检查组件是否已挂载。

你试试这个方法:

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的基础上获取数据,则添加回调useEffects[id]。然后它无法显示警告 React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

只需将函数作为参数传递给useEffects数组…

useEffect(() => {
functionName()
}, [functionName])

好吧,如果你想以不同的方式研究这个问题,你只需要知道React有哪些非exhaustive-deps的选项。你不应该在效果中使用闭包函数的原因之一是在每次渲染时,它都会被重新创建/销毁。

因此,挂钩中有多个React方法被认为是稳定且未耗尽的,您不必应用于useEffect依赖项,反过来也不会破坏react-hooks/exhaustive-deps的规则参与。例如,useReduceruseState的第二个返回变量是一个函数。

const [,dispatch] = useReducer(reducer, {});


useEffect(() => {
dispatch(); // Non-exhausted - ESLint won't nag about this
}, []);

因此,反过来,您可以让所有外部依赖项与您当前的依赖项共存于您的减速机函数中。

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]);

如果您在useEffect中使用的变量在组件中定义或作为道具传递给组件,则会发生此警告。由于您在同一组件中定义了fetchBusinesses(),因此必须将其传递给依赖数组。

但是如果你导入fetchBusinesses()然后在useEffect中使用它,你不需要将其添加到依赖数组中。这就是我们实际设置Redux应用程序的方式:我们总是导入我们的操作创建者并在useEffect中运行它,而不将其添加到依赖数组中。

useMemo也是如此。

在我的例子中,它对我的局部变量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])

您可以通过传递对其的引用来摆脱此ES-lint警告:

下面提到的示例,但是您可以在此链接上观看解决方案:https://www.youtube.com/watch?v=r4A46oBIwZk&;t=8s

警告: 第13:8行:React HookReact.use效果缺少依赖项:“历史”和“当前用户?.角色”。要么包含它们,要么删除依赖数组react-Hook/Extive-deps

React.useEffect(() => {
if (currentUser?.role !== "Student") {
return history.push("/")
}
}, [])

分辨率: 第1步:将业务逻辑移动到单独的const。

现在的警告是:React HookReact.use效果有一个缺失的依赖项:“角色检查”。

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函数。 这意味着在每个渲染中它都会声明触发钩子的新函数。

有两种方法可以解决这个问题。

  1. fetchBusinesses函数声明移出组件。

  2. useCallback钩子包装fetchBusinesses函数。

第一种选择更可取。

这不是特定于问题用例的答案,而是更一般的情况,并涵盖当useEffects或提取和导入不是warking时的情况。 引用地址:

有时情况是useEffects应该有空数组,你仍然想在状态的useEffects部分中使用,但仍然不想将它们作为依赖项注入,你也可以尝试useCallback,现在反应是抱怨useCallback的依赖项,你卡住了。 在这种情况下,在某些情况下,您可以使用useRef。例如:

const locationRef = useRef(location);
useEffect(()=>{
const qs = locationRef.current.search
...
},[])

使用此技术时应小心,并注意use Ref不会激活渲染过程。

useEffect()中使用UseEffectsfetchBusinesses调用函数声明,在调用函数名称之后声明一个const变量,

useEffect(()=>{
const fetchBusinesses=()=>{
console.log(useeffect fetchbussinesses functions)
}
fetchBusinesses();
},[declare the variable used in useeffect hooks ])

您正在使用useEffect,当您这样做时,您经常希望使用一些在组件中用作道具或状态的变量。

eslint中内置了一个规则,它希望您引用useEffect依赖数组中的任何不同的道具或状态片段。这是控制useEffect何时执行的数组。该规则希望看到它列在决定何时重新运行useEffect函数的数组中。

所以你需要添加[fetchBusinesses],警告应该消失了。

现在,为什么那个规则要我们把它放在那里?

在某些情况下,使用useEffect并且没有正确列出数组中的所有状态和道具可能会导致奇怪且难以调试的问题。

所以这个规则是为了帮助避免那些难以理解的问题,可以提出useEffect

现在,任意添加到该数组也会导致错误。所以无论哪种方式,你都会遇到你必须解决的错误。根据你的评论,这似乎为你解决了问题,但我想进一步调查,看看你是否偶然在fetchBusinesses函数添加到useEffect数组后,在Chrome的网络选项卡中获得了第二个GET请求。

搜索关键字以了解有关每个警告的更多信息。 要忽略,请在前面的行中添加//eslint-disable-next-line。

例如:使用效果中使用的函数正在引起警告

useEffect(() => {
handleConnectWallet();
}, []);

要忽略警告,我们只需在警告行之前添加“//eslint-disable-next-line”即可。

useEffect(() => {
handleConnectWallet();
// eslint-disable-next-line
}, []);

如果要禁用此无用消息,只需在文件开头添加此消息。

/* eslint-disable react-hooks/exhaustive-deps */

这将禁用整个文件的警告。

在您的项目中禁用此警告

将此"react-hooks/exhaustive-deps": "off"添加到.eslintrc.js文件