这些依赖是什么?
useEffect
接受一个可选的第二个参数,即一个依赖项数组。
useEffect
钩子的依赖项告诉它在其中一个依赖项更改时运行该效果。
如果不将可选的第二个参数传递给 useEffect 钩子,则每次组件重新呈现时都会执行它。空的依赖项数组指定您只想在组件的初始呈现后运行该效果一次。在这种情况下,
useEffect
钩子几乎会像类组件中的 componentDidMount 一样运行。
为什么建议对它们进行详尽说明?
效果从定义它们的渲染中查看
props
和
state
。因此,当您在
useEffect
钩子的回调函数中使用来自参与React数据流的函数组件范围的东西,例如props或state时,该回调函数将关闭该数据,并且除非使用新的值定义新效果的props和状态,否则您的效果将查看旧的props和state值。
以下代码片段演示了如果您在
useEffect
钩子的依赖项上撒谎会发生什么问题。
在以下代码片段中,有两个组件,
App
和
User
。
App
组件有三个按钮,并维护由
User
组件显示的用户的 id。用户 id 作为 prop 从
App
组件传递到
User
组件,
User
组件从
jsonplaceholder
API 获取传递的 id 对应的用户。
现在以下代码片段的问题是它不能正确工作。原因在于它关于
useEffect
钩子的依赖关系有误。
useEffect
钩子依赖于
userID
prop 来从 API 中获取用户,但由于我忘记将
userID
添加为依赖项,
useEffect
钩子不会在每次更改
userID
prop 时执行。
function User({userID}) {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
if (userID > 0) {
fetch(`https://jsonplaceholder.typicode.com/users/${userID}`)
.then(response => response.json())
.then(user => setUser(user))
.catch(error => console.log(error.message));
}
}, []);
return (
<div>
{user ? <h1>{ user.name }</h1> : <p>No user to show</p>}
</div>
);
}
function App() {
const [userID, setUserID] = React.useState(0);
const handleClick = (id) => {
setUserID(id);
};
return(
<div>
<button onClick={() => handleClick(1)}>User with ID: 1</button>
<button onClick={() => handleClick(2)}>User with ID: 2</button>
<button onClick={() => handleClick(3)}>User with ID: 3</button>
<p>Current User ID: {userID}</p>
<hr/>
<User userID={userID} />
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
上面的代码片段展示了如果你在依赖项上撒谎可能会出现的少数问题,这就是为什么
你不能跳过或者撒谎依赖项对于
useEffect
钩子或任何其他有依赖项数组的钩子,例如
useCallback
钩子。
要修复前面的代码片段,你只需要将
userID
作为
useEffect
钩子的依赖项添加到依赖项数组中,这样每当
userID
属性更改并使用该属性相等的ID获取新用户时,它就会执行。
function User({userID}) {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
if (userID > 0) {
fetch(`https://jsonplaceholder.typicode.com/users/${userID}`)
.then(response => response.json())
.then(user => setUser(user))
.catch(error => console.log(error.message));
}
}, [userID]);
return (
<div>
{user ? <h1>{ user.name }</h1> : <p>No user to show</p>}
</div>
);
}
function App() {
const [userID, setUserID] = React.useState(0);
const handleClick = (id) => {
setUserID(id);
};
return(
<div>
<button onClick={() => handleClick(1)}>User with ID: 1</button>
<button onClick={() => handleClick(2)}>User with ID: 2</button>
<button onClick={() => handleClick(3)}>User with ID: 3</button>
<p>Current User ID: {userID}</p>
<hr/>
<User userID={userID} />
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
在您的情况下,如果您在
useEffect
钩子的依赖数组中跳过添加
props.userID
,则当 prop
userID
改变时,您的 effect 将不会获取新数据。
要了解省略
useEffect
钩子的依赖项的负面影响,请参见:
我该怎么干净地解决我的问题呢?
由于您的 effect 取决于 prop 值
userID
,因此您应将其包含在依赖项数组中,以便在每次
userID
更改时始终获取新数据。
将
props.userID
添加为
useEffect
hook 的依赖项将在每次
props.userID
更改时触发该 effect ,但问题在于你不必要地在
useEffect
中使用了
username
。应该将其删除,因为这不是必需的,由于
username
值没有并且不应该决定何时获取新的用户数据。你只是想让 effect 在
props.userID
更改时运行。
您还可以通过使用 useReducer
hook 来管理和更新状态,使操作与状态更新解耦。
Edit
既然你只想在 userID
被 useEffect
hook 使用时运行 effect,那么在你的情况下,将空数组作为第二个参数并忽略 eslint 警告是可以的。你也可以不省略 useEffect
hook 的任何依赖项,并在 useEffect
hook 中使用某些条件进行评估,在 effect 运行第一次并更新状态后返回 false。
个人建议尝试更改组件结构,以避免首先出现此类问题。
useEffect
的依赖数组中,但只在初始渲染时获取用户,使用一个条件,在useEffect
至少执行一次后评估为 false。 - YousafReact Hook useEffect缺少依赖项:'props.userID'和'username'。要么包含它们,要么删除依赖项数组。eslint(react-hooks / exhaustive-deps)
- maximeuseEffect
dep's array differ from other hooks likeuseCallback
anduseMemo
that could cause stale values if every dep isnt included.useEffect
relies more on business logic and therefore imo a linter shouldn't decide your business logic. for the most part I usually list all the deps if possible, otherwise I ignore the rule for that scenario and leave a comment as to why.eslint-disable-next-line react-hooks/exhaustive-deps
- Matt Aft