使用快乐的 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的主要内容,如果未能解决你的问题,请参考以下文章

如何在反应中只运行一次功能

禁用未找到 eslint 规则的警告消息

在 Vue-cli 项目中应用 eslint-loader 选项,以便尊重 eslint 配置

使用插件 eslint-plugin-jsx-a11y 运行全局 ESLint

Vue取消eslint语法限制

Vue取消eslint语法限制