使用快乐的 ESLint 运行一次 useEffect
Posted
技术标签:
【中文标题】使用快乐的 ESLint 运行一次 useEffect【英文标题】:Running a useEffect once with happy ESLint 【发布时间】:2021-04-17 23:27:39 【问题描述】:这是一个 React 风格的问题。
TL;DR 从 React 的 useState
中获取 set
函数。如果该函数在每次渲染时都会“更改”,那么在 useEffect
中使用它的最佳方法是什么,并且效果只运行一次?
解释我们有一个 useEffect 需要运行一次(它获取 Firebase 数据),然后将该数据设置为应用程序状态。
这是一个简化的示例。我们正在使用little-state-machine,updateProfileData
是更新 JSON 状态的“配置文件”部分的操作。
const MyComponent = () =>
const actions, state = useStateMachine(updateProfileData, updateLoginData);
useEffect(() =>
const get_data_async = () =>
const response = await get_firebase_data();
actions.updateProfileData( user: response.user );
;
get_data_async();
, []);
return (
<p>Hello, world!</p>
);
然而,ESLint 不喜欢这样:
React Hook useEffect has a missing dependency: 'actions'. Either include it or remove the dependency array
这是有道理的。问题是这样的:actions
每次渲染都会改变——更新状态会导致重新渲染。无限循环。
取消引用 updateProfileData
也不起作用。
使用类似这样的东西是一种好习惯:单次运行useEffect
?
可能/可能不起作用的概念代码:
const useSingleEffect = (fxn, dependencies) =>
const [ hasRun, setHasRun ] = useState(false);
useEffect(() =>
if(!hasRun)
fxn();
setHasRun(true);
, [...dependencies, hasRun]);
;
// then, in a component:
const MyComponent = () =>
const actions, state = useStateMachine(updateProfileData, updateLoginData);
useSingleEffect(async () =>
const response = await get_firebase_data();
actions.updateProfileData( user: response.user );
, [actions]);
return (
<p>Hello, world!</p>
);
但是到那时,为什么还要关心依赖数组呢?显示的初始代码有效且有意义(闭包保证正确的变量/函数),ESLint 只是建议不要这样做。
就像 React useState
的第二个返回值在每次渲染时都改变了一样:
const [ foo, setFoo ] = useState(null);
// ^ this one
如果这改变了每次渲染,我们如何用它运行一次效果?
【问题讨论】:
【参考方案1】:忽略行的 eslint 规则
如果您真的希望效果在组件安装时只运行一次,那么使用空依赖数组是正确的。您可以禁用该行的 eslint 规则以忽略它。
useEffect(() =>
const get_data_async = () =>
const response = await get_firebase_data();
actions.updateProfileData( user: response.user );
;
get_data_async();
// NOTE: Run effect once on component mount, please
// recheck dependencies if effect is updated.
// eslint-disable-next-line react-hooks/exhaustive-deps
, []);
注意:如果您稍后更新效果并且它需要在其他依赖项之后运行,那么这个禁用的注释可能会掩盖未来的错误,所以我建议留下一个相当overt 评论覆盖已建立的 linting 规则的原因。
自定义挂钩逻辑
或者,您可以使用 react ref 来表示初始渲染。这比使用某些状态来保存值更可取,因为更新它会触发不必要的渲染。
const MyComponent = () =>
const actions, state = useStateMachine(updateProfileData, updateLoginData);
const initialRenderRef = useRef(true);
useEffect(() =>
const get_data_async = () =>
const response = await get_firebase_data();
actions.updateProfileData( user: response.user );
;
if (initialRenderRef.current)
initialRenderRef.current = false;
get_data_async();
, [actions]); // <-- and any other dependencies the linter complains about
return (
<p>Hello, world!</p>
);
是的,如果您发现在您的代码库中反复使用这种“单次运行逻辑”,那么您绝对可以将其纳入自定义挂钩中。
【讨论】:
以上是关于使用快乐的 ESLint 运行一次 useEffect的主要内容,如果未能解决你的问题,请参考以下文章
在 Vue-cli 项目中应用 eslint-loader 选项,以便尊重 eslint 配置