如果没有,则仅可以安全地从依赖项列表中省略功能 其中(或其调用的函数)引用道具,状态或 从中得出的值。
然后,FAQ继续给出一个示例,其中省略了该函数,并说代码包含错误。但是,常见问题解答从未提及该错误是什么。
我做了一个类似的示例,其中我创建了一个使用两个状态的函数。然后从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>
);
}
所有预期的语义都得到满足。有趣的是,使用按钮更改名称状态不会触发警报,但是在触发警报时始终使用正确的名称。
上面的代码在useEffect()的依赖项列表上产生承诺的 react-hooks / exhaustive-deps 警告。警告说,该钩子缺少greet()
的依赖项。警告的自动修复是将greet添加为依赖项。
useEffect(
function greetOnGreetingChange() {
greet();
},
[greeting, greet]
);
但是,这会在greet()
函数上产生另一个ESLint错误。该错误表明该函数在每个渲染器上都被调用。单击生成随机按钮可以确认此意外行为。 ESLint建议将greet()
函数包装为useCallback
效果,例如:
const greet = useCallback(function greet() {
alert(`${greeting}, ${name}.`)
}, [greeting]);
但是在乌龟方面,ESLint抱怨说useCallback效果缺少name
依赖性。添加该依赖关系会破坏预期的语义,因为该警报现在会在 name 状态更新时随时触发。
这是一个简单的,有些人为的示例,但是它经常出现在我一直在研究的多个代码库中。场景很简单。您在组件内部使用函数有一些状态。该函数在组件内的多个位置(在useEffect挂钩的内部和外部)都被调用。您希望useEffect挂钩仅在单个道具或状态发生变化时才调用该函数。
React的文档建议最好的解决方案是将功能移到useEffect挂钩内。但这将阻止它在组件中的其他位置使用。下一个建议是将功能包括在依赖列表中,并在需要时使用useCallback()挂钩将其包装。但是,在许多情况下,这可能会引入不受欢迎的行为,或者只是将ESLint错误引导至useCallback()。
React想要防范的原始代码中的“ bug ”是什么?除了禁用ESLint检查之外,还有其他解决方案吗?
答案 0 :(得分:0)
基于Dan Abramov Use Effect articel(由@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>
)
}
由于保证dispatch
在所有渲染中始终保持一致,因此效果只会在更改为 greeting 状态时触发,而警报仍将捕获并显示对的任何更改>名称状态。