【发布时间】:2020-04-16 20:50:11
【问题描述】:
仅安全地从依赖项列表中省略一个函数(如果没有) 其中(或由它调用的函数)引用道具、状态或 从它们派生的值。
FAQ 然后继续给出一个示例,其中该函数被省略,并说代码包含错误。但是,常见问题解答从未提及错误是什么。
我做了一个类似的例子,我创建了一个使用两个状态的函数。然后从 useEffect 挂钩调用该函数,该挂钩在其依赖项列表中只有一个状态。然而,即使有关于缺少依赖项的承诺 ESLint 警告,该函数和 useEffect 挂钩仍按预期工作。
预期语义:
- 点击问候按钮时显示警报(直接函数调用)
- “问候”状态更改时显示警报(通过 useEffect)
- 名称更改时不显示警告。
- 每当显示问候语时,都会使用最后指定的名称。
代码:
export function UseEffectEx(props) {
const [greeting, setGreeting] = useState("Hello");
const [name, setName] = useState("John");
const [randomNumber, setRandomNumber] = useState(Math.random());
function greet() {
alert(`${greeting}, ${name}.`);
}
useEffect(
function greetOnGreetingChange() {
greet();
},
[greeting]
);
return (
<div>
<button onClick={greet}>Greet</button>
<button onClick={() => setGreeting("Hello")}>
set greeting to 'Hello'
</button>
<button onClick={() => setGreeting("Goodbye")}>
set greeting to 'Goodbye'
</button>
<button onClick={() => setName("John")}>set name to 'John'</button>
<button onClick={() => setName("Jane")}>set name to 'Jane'</button>
<button onClick={() => setRandomNumber(Math.random())}>
generate random
</button>
<p>Random number = ${randomNumber}</p>
</div>
);
}
所有预期的语义都得到满足。 Curcily,使用按钮更改名称状态不会触发警报,但触发警报时始终使用正确的名称。
ESLint 警告
上面的代码在 useEffect() 的依赖列表上产生了承诺的 react-hooks/exhaustive-deps 警告。警告说钩子缺少greet() 的依赖项。警告的自动修复是添加 greet 作为依赖项。
useEffect(
function greetOnGreetingChange() {
greet();
},
[greeting, greet]
);
但是,这会产生另一个 ESLint 错误,这次是在 greet() 函数上。该错误表明该函数在每次渲染时都被调用。单击 generate random 按钮可确认此意外行为。 ESLint 建议将greet() 函数包裹在useCallback 效果中,例如:
const greet = useCallback(function greet() {
alert(`${greeting}, ${name}.`)
}, [greeting]);
但是在海龟的情况下,ESLint 抱怨 useCallback 效果缺少 name 依赖项。添加该依赖项会破坏预期的语义,因为现在警报将在 name 状态更新时触发。
解决方案?
这是一个简单的、有点做作的示例,但它经常出现在我一直在处理的多个代码库中。场景很简单。您在组件内使用函数有一些状态。该函数在组件内的多个位置被调用,包括在 useEffect 钩子内部和外部。您希望 useEffect 挂钩仅在单个 prop 或状态发生更改时调用该函数。
React 的文档建议最好的解决方案是将函数移动到 useEffect 挂钩中。但这会阻止它在组件内的其他地方使用。下一个建议是将函数包含在依赖列表中,并在需要时用 useCallback() 钩子包装它。然而,在许多情况下,这要么引入了不受欢迎的行为,要么只是将 ESLint 错误引导到 useCallback()。
React 想要防范的原始代码中的“bug”是什么?除了禁用 ESLint 检查之外,还有其他解决方案吗?
【问题讨论】:
-
来自 Dan Abramov 本人:overreacted.io/a-complete-guide-to-useeffect
标签: reactjs react-hooks