使用Happy ESLint运行一次useEffect

这是一个反应风格的问题。

tl;dr从Reaction的useState获取set函数。如果函数在每次渲染时都会更改,那么在只运行一次效果情况下,在useEffect中使用该函数的最佳方式是什么?

解释我们有一个useEffect需要运行一次(它获取Firebase数据),然后将该数据设置为应用程序状态。

这里有一个简化的示例。我们正在使用little-state-machine,updateProfileData是用于更新JSON状态的";Profile";部分的操作。


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只是建议不要这样做。

这就像ReactionuseState的第二个返回值在每次呈现时都发生了变化:

const [ foo, setFoo ] = useState(null);
//             ^ this one

如果更改了每个渲染,我们如何对其运行一次效果?


解决方案

忽略线路

的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
}, []);

注意:如果您稍后更新了效果,并且它需要在其他依赖项之后运行,则此禁用的注释可能会掩盖未来的错误,因此我建议您留下一个相当公开的注释,说明覆盖已建立的内联规则的原因。

自定义挂钩逻辑

或者,您可以使用反应引用来表示初始呈现。这比使用某种状态保存该值更可取,因为更新它将触发不必要的呈现。

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>
  );
}

是的,如果您发现在代码库中反复使用的模式,您绝对可以将这个单运行逻辑分解到一个自定义钩子中。

相关文章