Bri*_*HVB 9 reactjs react-hooks
只有当某个函数(或其调用的函数)中没有任何内容引用从它们派生的 props、状态或值时,才可以安全地从依赖项列表中省略该函数。
常见问题解答接着给出了一个示例,其中省略了该函数,并表示代码包含错误。然而,常见问题解答从未提及该错误是什么。
我做了一个类似的例子,我创建了一个使用两个状态的函数。然后从 useEffect 钩子调用该函数,该钩子的依赖项列表中只有一部分状态。然而,即使存在针对缺失依赖项所承诺的 ESLint 警告,该函数和 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>
);
}
Run Code Online (Sandbox Code Playgroud)
所有预期的语义都得到满足。当然,使用按钮更改名称状态不会触发警报,但触发警报时始终使用正确的名称。
上面的代码在 useEffect() 的依赖项列表上产生了承诺的react-hooks/exhaustive-deps警告。该警告表示该钩子缺少greet(). 警告的自动修复是将greet 添加为依赖项。
useEffect(
function greetOnGreetingChange() {
greet();
},
[greeting, greet]
);
Run Code Online (Sandbox Code Playgroud)
然而,这会产生另一个 ESLint 错误,这次是在函数上greet()。该错误表明该函数在每次渲染时都被调用。单击生成随机按钮确认了这种意外行为。ESLint 建议该greet()函数应该包装在一个useCallback效果中,例如:
const greet = useCallback(function greet() {
alert(`${greeting}, ${name}.`)
}, [greeting]);
Run Code Online (Sandbox Code Playgroud)
但在乌龟这种情况下,ESLint 抱怨 useCallback 效果缺少name依赖项。添加该依赖项会破坏预期的语义,因为现在只要名称状态更新就会触发警报。
这是一个简单的、有些人为的示例,但它经常出现在我一直在研究的多个代码库中。场景很简单。您在组件内使用函数有一些状态。该函数在组件内的多个位置(在 useEffect 挂钩内部和外部)被调用。您希望 useEffect 挂钩仅在单个 prop 或状态更改时调用该函数。
React 的文档建议最好的解决方案是将函数移至 useEffect 挂钩内。但这会阻止它在组件内的其他地方使用。下一个建议是将该函数包含在依赖项列表中,并在需要时使用 useCallback() 挂钩将其包装。然而,在许多情况下,这要么会引入不受欢迎的行为,要么只是将 ESLint 错误引导至 useCallback()。
React 想要防范的原始代码中的“ bug ”是什么?除了禁用 ESLint 检查之外还有其他解决方案吗?
根据Dan Abramov 使用效果文章(由 @Bennett Dams 提供),没有很好的解决方案。
ESList 警告要防范的错误是,当名称状态更改时,效果不会触发。然而,由于这种行为是有意为之的,所以更大的问题是代码的语义取决于状态更改,有时会导致更新,有时则不会。这似乎与 React Hooks 的潮流背道而驰。
是的——虽然有点难看,但是使用useReducer可以实现想要的语义。下面的代码很丑陋,因为我们使用减速器来触发函数,而不是按预期使用它,即更新状态。
function Example(props) {
const [greeting, setGreeting] = useState('Hello');
const [name, setName] = useState('John');
const [randomNumber, setRandomNumber] = useState(Math.random());
const [_, dispatch] = useReducer(reducer, {});
function greet() {
alert(`${greeting}, ${name}.`)
}
function reducer(state, action) {
if (action.type === 'alert') {
greet();
}
return state;
}
useEffect(function greetOnGreetingChange() {
dispatch({type: 'alert'})
}, [dispatch, greeting]);
return (
<div>
<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>
<button onClick={() => greet()}>greet</button>
<p>Random number = ${randomNumber}</p>
</div>
)
}
Run Code Online (Sandbox Code Playgroud)
由于dispatch保证在渲染之间始终保持一致,因此效果只会在问候状态发生更改时触发,而警报仍将捕获并显示名称状态的任何更改。
| 归档时间: |
|
| 查看次数: |
3793 次 |
| 最近记录: |