如何修復 “React Hook useEffect Has a Missing Dependency” 錯誤

如何修復 "React Hook useEffect Has a Missing Dependency" 錯誤

React useEffect 鉤子自 16.8 版推出以來,已成為 React 庫中的一項流行功能。它使開發人員能夠在功能元件中執行副作用,如獲取資料、更新 DOM 和訂閱事件。

不過, useEffect 鉤子的使用有時會很棘手。開發人員遇到的一個常見錯誤是 “React Hook useEffect has a missing dependency. Either include it or remove the dependency array” 的錯誤。

在本文中,我們將討論導致該錯誤的原因,並提供如何修復該錯誤的各種解決方案。

什麼原因導致 “React Hook useEffect Has a Missing Dependency” 錯誤?

useEffect 鉤子的依賴關係陣列不完整或丟失時,就會出現 “React Hook useEffect has a missing dependency”(React 鉤子使用效果缺少依賴關係)錯誤。

依賴關係陣列是 useEffect 鉤子中的第二個引數,用於指定效果所依賴的變數。這意味著當依賴關係陣列中指定的變數值發生變化時,將重新執行效果。

如果效果所依賴的變數未包含在依賴關係陣列中,那麼當變數值發生變化時,效果可能不會被重新執行。這可能會導致應用程式出現意外行為和錯誤。

此錯誤不是 React 錯誤,而是 ESLint 錯誤。ESLint 專門為 React 提供了一個外掛,其中包含一套規則,旨在幫助開發人員編寫更好的 React 程式碼。其中一個規則是 "react-hooks/exhaustive-deps" 規則,它可以檢測 “React Hook useEffect has a missing dependency” 錯誤。

舉例來說,我們來看一個具有計數狀態的功能元件。每當計數狀態發生變化時,該元件也要向控制檯記錄一條包含 count 值的訊息:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`You clicked ${count} times`);
}, []);
return (
<div>
<h1>Hello World</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default App;
import { useState, useEffect } from 'react'; const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.log(`You clicked ${count} times`); }, []); return ( <div> <h1>Hello World</h1> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default App;
import { useState, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`You clicked ${count} times`);
}, []);
return (
<div>
<h1>Hello World</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default App;

在上例中,您有一個使用 useStateuseEffect 掛鉤的功能元件。每當 count 狀態變數的值發生變化時, useEffect 鉤子就會記錄一條資訊。

但請注意,count 變數並沒有列在 useEffect 鉤子的第二個引數陣列(依賴陣列)中。這將觸發 “React Hook useEffect has a missing dependency” 錯誤。

React 鉤子 useEffect 有一條缺少依賴關係的錯誤資訊

React 鉤子 useEffect 有一條缺少依賴關係的錯誤資訊

修復 “React Hook useEffect Has a Missing Dependency” 錯誤的 3 種方法

根據您希望使用的方法,可以用不同的方式來修復這個錯誤。以下是各種方法。

  • 包含所有缺失的依賴關係
  • 在處理物件和函式時使用 memoization 鉤子
  • 禁用 ESLint 規則

1. 將缺失的依賴關係新增到 useEffect 依賴關係陣列中

解決這一錯誤的直接方法是將 useEffect 鉤子中使用的所有依賴關係都加入依賴關係陣列。那麼您可能會問,我如何知道依賴關係呢?

要識別缺失的依賴關係,您需要檢視 useEffect 鉤子中使用的變數或值。如果這些變數或值會隨著時間的推移而發生變化,那麼它們就應該包含在依賴關係陣列中。

例如,在前面提供的程式碼片段中, useEffect 鉤子內部使用了 count 變數,但它並沒有包含在依賴關係陣列中。這意味著如果 count 變數發生變化,useEffect 鉤子將不會被重新執行,元件可能會出現過時資料或其他問題。

要解決這個錯誤,我們可以將 count 變數新增到依賴關係陣列中,就像下面這樣:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`You clicked ${count} times`);
}, [count]);
return (
<div>
<h1>Hello World</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default App;
import { useState, useEffect } from 'react'; const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.log(`You clicked ${count} times`); }, [count]); return ( <div> <h1>Hello World</h1> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default App;
import { useState, useEffect } from 'react';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`You clicked ${count} times`);
}, [count]);
return (
<div>
<h1>Hello World</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default App;

通過將 count 變數新增到依賴關係陣列,我們可以告訴 React,只要 count 變數發生變化,就會重新執行 useEffect 鉤子。

這將確保元件始終擁有最新資料,並避免出現 “React Hook useEffect has a missing dependency” 的錯誤。

如果您有多個依賴項,請將它們新增到依賴項陣列中,並用逗號將它們分開:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState, useEffect } from 'react';
const App = () => {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
const handleFirstNameChange = (event) => {
setFirstName(event.target.value);
};
const handleLastNameChange = (event) => {
setLastName(event.target.value);
};
return (
<div>
<label>
First Name:
<input type="text" value={firstName} onChange={handleFirstNameChange} />
</label>
<label>
Last Name:
<input type="text" value={lastName} onChange={handleLastNameChange} />
</label>
<p>Full Name: {fullName}</p>
</div>
);
};
export default App;
import { useState, useEffect } from 'react'; const App = () => { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [fullName, setFullName] = useState(''); useEffect(() => { setFullName(`${firstName} ${lastName}`); }, [firstName, lastName]); const handleFirstNameChange = (event) => { setFirstName(event.target.value); }; const handleLastNameChange = (event) => { setLastName(event.target.value); }; return ( <div> <label> First Name: <input type="text" value={firstName} onChange={handleFirstNameChange} /> </label> <label> Last Name: <input type="text" value={lastName} onChange={handleLastNameChange} /> </label> <p>Full Name: {fullName}</p> </div> ); }; export default App;
import { useState, useEffect } from 'react';
const App = () => {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
const handleFirstNameChange = (event) => {
setFirstName(event.target.value);
};
const handleLastNameChange = (event) => {
setLastName(event.target.value);
};
return (
<div>
<label>
First Name:
<input type="text" value={firstName} onChange={handleFirstNameChange} />
</label>
<label>
Last Name:
<input type="text" value={lastName} onChange={handleLastNameChange} />
</label>
<p>Full Name: {fullName}</p>
</div>
);
};
export default App;

2. 處理物件和函式

在使用物件和陣列時,僅將它們新增到依賴陣列中是不夠的,還需要將它們 memoize 或移動到 useEffect 鉤子中或元件外部,以避免不必要的重新呈現。

這是因為在 JavaScript 中,物件和陣列是通過引用進行比較的,每次都指向記憶體中的不同位置,因此每次呈現時其值都會改變,從而導致無限的重新呈現迴圈。

下面是一個導致錯誤的示例:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState, useEffect } from 'react';
const App = () => {
const [user, setUser] = useState({});
// 👇️this will change on every render
let newUser = { name: 'Jane', age: 28 };
useEffect(() => {
setUser(newUser);
}, [newUser]);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default App;
import { useState, useEffect } from 'react'; const App = () => { const [user, setUser] = useState({}); // 👇️this will change on every render let newUser = { name: 'Jane', age: 28 }; useEffect(() => { setUser(newUser); }, [newUser]); return ( <div> <h1>Hello World</h1> </div> ); }; export default App;
import { useState, useEffect } from 'react';
const App = () => {
const [user, setUser] = useState({});
// 👇️this will change on every render
let newUser = { name: 'Jane', age: 28 };
useEffect(() => {
setUser(newUser);
}, [newUser]);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default App;

您可以通過將物件移入 useEffect 鉤子或將其移出元件來解決這個錯誤:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState, useEffect } from 'react';
const App = () => {
const [user, setUser] = useState({});
useEffect(() => {
let newUser = { name: 'Jane', age: 28 };
setUser(newUser);
}, []);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default App;
import { useState, useEffect } from 'react'; const App = () => { const [user, setUser] = useState({}); useEffect(() => { let newUser = { name: 'Jane', age: 28 }; setUser(newUser); }, []); return ( <div> <h1>Hello World</h1> </div> ); }; export default App;
import { useState, useEffect } from 'react';
const App = () => {
const [user, setUser] = useState({});
useEffect(() => {
let newUser = { name: 'Jane', age: 28 };
setUser(newUser);
}, []);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default App;

解決這一問題的更好方法是使用備忘錄化鉤子,如對物件使用 useMemo,對函式使用 useCallback。這將有助於在元件和依賴關係陣列中保留物件或函式。

注:memoization 鉤子是一組鉤子,可讓您快取昂貴的計算結果,避免不必要的重新計算。

當您使用 useMemo 鉤子對物件進行記憶化處理時,您的程式碼就會變成這樣:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState, useEffect, useMemo } from 'react';
const App = () => {
const [user, setUser] = useState({});
const newUser = useMemo(() => {
return { name: 'John', age: 30 };
}, []);
useEffect(() => {
setUser(newUser);
}, [newUser]);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default App;
import { useState, useEffect, useMemo } from 'react'; const App = () => { const [user, setUser] = useState({}); const newUser = useMemo(() => { return { name: 'John', age: 30 }; }, []); useEffect(() => { setUser(newUser); }, [newUser]); return ( <div> <h1>Hello World</h1> </div> ); }; export default App;
import { useState, useEffect, useMemo } from 'react';
const App = () => {
const [user, setUser] = useState({});
const newUser = useMemo(() => {
return { name: 'John', age: 30 };
}, []);
useEffect(() => {
setUser(newUser);
}, [newUser]);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default App;

同樣,在使用函式時,可以使用 useCallback 鉤子。

3. 禁用 ESLint 規則

“React Hook useEffect has a missing dependency” 錯誤是一個 ESLint 警告錯誤,這意味著我們可以禁用該規則,這樣它就不會丟擲該錯誤。並不建議在所有情況下都採用這種方法,但如果您確定缺少的依賴關係不是一個問題,這種方法可以快速解決問題。

可以在依賴關係陣列行前新增以下注釋。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps

下面就是一個例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
useEffect(() => {
console.log(`You clicked ${count} times`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => { console.log(`You clicked ${count} times`); // eslint-disable-next-line react-hooks/exhaustive-deps }, []);
useEffect(() => {
console.log(`You clicked ${count} times`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

需要注意的是,如果不小心禁用 ESLint 規則,將來可能會導致其他問題。請確保在禁用規則前徹底瞭解其後果,並儘可能考慮其他解決方案。

小結

“React Hook useEffect has a missing dependency” 錯誤是 React 開發人員在使用 useEffect 掛鉤時面臨的常見問題。

在修復該錯誤時,重要的是要考慮針對特定用例的最佳方法。一般來說,最好避免禁用導致該錯誤的 ESLint 規則,因為這會在將來導致其他問題。相反,可以嘗試在依賴關係陣列中加入缺失的依賴關係,或使用正確的 memoization 鉤子來解決這個問題。

現在輪到你了: 您遇到過這個問題嗎?你是如何解決的?您還使用過本文未涉及的其他方法嗎?請在評論中告訴我們!

評論留言