如何在React的useEffect中仅调用一次加载函数

524
useEffect React钩子将在每次更改时运行传入的函数。这可以优化为仅在期望的属性更改时调用它。
如果我想从componentDidMount调用初始化函数而不在更改时再次调用它怎么办?假设我想要加载一个实体,但是加载函数不需要组件中的任何数据。我们如何使用useEffect钩子来实现这一点?
class MyComponent extends React.PureComponent {
    componentDidMount() {
        loadDataOnlyOnce();
    }
    render() { ... }
}

使用Hooks可以让它看起来像这样:

function MyComponent() {
    useEffect(() => {
        loadDataOnlyOnce(); // this will fire on every change :(
    }, [...???]);
    return (...);
}
18个回答

2

这并不是直接回答你的问题,但可能会产生相同的预期效果,只运行一次函数且在第一次渲染后。这与componentDidMount函数非常相似。这里使用useState而不是useEffect来避免依赖lint错误。你只需将自执行匿名函数作为useState的第一个参数传递即可。顺便说一下,我不确定为什么React不直接提供一个可以实现这个功能的钩子。

import React, { useState } from "react"

const Component = () => {

  useState((() => {
    console.log('componentDidMountHook...')
  }))

  return (
    <div className='component'>Component</div>
  )
}

export default Component

1

经过在互联网上花费一些时间后,我发现useEffect在组件挂载时只触发一次,然后组件卸载并重新挂载,useEffect再次触发。你需要查看更多React文档,了解他们为什么这样做。

因此,我使用了自定义hook来处理这个问题。在卸载时,您必须更改useRef状态。在这种情况下,不要忘记return语句:当组件卸载时,useEffect在返回后运行清理函数。

import React, { useEffect, useRef } from "react"

const useDidMountEffect = (
  func: () => void,
  deps: React.DependencyList | undefined
) => {
  const didMount = useRef(false)

  useEffect(() => {
    if (didMount.current) {
      func()
    }
    return () => {
      didMount.current = true
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps)
}

export default useDidMountEffect

像普通的useEffect一样使用它:

  useDidMountEffect(() => {
    // your action    
  }, [])

1
你应该使用下面的第一种方式。 useEffect钩子有3种使用方式。
  1. 在组件渲染时只运行一次:

     useEffect(()=>{
         // 你的代码
     },[])
    
  2. 每当 x1、x2 等依赖项发生变化时都会运行:

     useEffect(()=>{
     // 你的代码
     },[x1,x2,...])
    
  3. 在更新任何内容和组件渲染时都会运行:

      useEffect(()=>{
     // 你的代码
      })
    

0
我发现使用lodash中的once函数可以简洁而优雅地解决这个问题。
import { once } from "lodash";
import { useEffect, useRef } from "react";

export const useEffectOnce = (cb: () => void) => {
  const callBackOnce = useRef(once(cb)).current;
  useEffect(() => callBackOnce(), [callBackOnce]);
};

0

window.onpageshow即使用户按下返回按钮导航到页面,也能正常工作,而不像将空数组作为useEffect钩子的第二个参数传递时,当通过返回按钮返回页面时不会触发(因此不会在每次初始页面加载时触发)。

 useEffect(() => {    
     window.onpageshow = async function() {
      setSomeState(false)
      let results = await AsyncFunction() 
       console.log(results, 'Fires on on first load, 
        refresh, or coming to the page via the back button')
    };
 };

0
在未来,我们将拥有useEffectEvent,请参见这篇官方React文章

使用一个名为useEffectEvent的特殊Hook,将这个非响应式逻辑从你的Effect中提取出来:

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme);
  });

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ All dependencies declared
  // ...

这里,onConnected 被称为 Effect 事件。它是你的 Effect 逻辑的一部分,但它的行为更像一个事件处理程序。其中的逻辑不是响应式的,它总是“看到”你的 props 和 state 的最新值。
在他们的示例中,即使 theme 更改(或回调函数在问题的情况下),效果事件也不会重新运行(即 useEffectEvent 没有任何依赖项)。
因此,为了回答这个问题(假设 loadDataOnlyOnce 取决于某些范围内的 props/state 或其他变量,它会一遍又一遍地执行;否则只需将其放在组件体外;React 反对使用 useCallback 进行流程控制):
function MyComponent() {
    const loadDataOnlyOnceEvent = useEffectEvent(loadDataOnlyOnce)

    useEffect(() => {
        loadDataOnlyOnceEvent();
    }, []);
    return (...);
}

0
你在useEffect中传入了一个空数组,所以它只会运行一次。
import React, { useEffect } from "react";

function MyComponent() {
  useEffect(() => {
    // This code will run only once when the component mounts
  }, []); // The empty dependency array means the effect runs only once

  return <div>{/* Your component JSX */}</div>;
}

-1
如果您只是在渲染后调用useEffect函数,那么您需要将一个空数组作为useEffect的第二个参数添加进去。
useEffect=(()=>{
   functionName(firstName,lastName);
},[firstName,lastName])

calling a custom hooks

adding a empty array for rendering


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接