Как вызвать функцию загрузки с помощью React useEffect только один раз

Хук useEffect React будет запускать переданную функцию при каждом изменении. Это можно оптимизировать, чтобы он вызывался только при изменении желаемых свойств.

Что если я хочу вызвать функцию инициализации из componentDidMount и не вызывать ее снова при изменениях? Допустим, я хочу загрузить объект, но функция загрузки не требует никаких данных от компонента. Как мы можем сделать это, используя хук useEffect?

class MyComponent extends React.PureComponent {
    componentDidMount() {
        loadDataOnlyOnce();
    }
    render() { ... }
}

С крючками это может выглядеть так:

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

Ответы

Ответ 1

Если вы хотите запустить только функцию, заданную для useEffect после первоначального рендеринга, вы можете дать ей пустой массив в качестве второго аргумента.

function MyComponent() {
  useEffect(() => {
    loadDataOnlyOnce();
  }, []);

  return <div> {/* ... */} </div>;
}

Ответ 2

TL; DR

useEffect(yourCallback, []) - вызовет обратный вызов только после первого рендера.

Детальное объяснение

useEffect запускается по умолчанию после каждого рендеринга компонента (таким образом вызывая эффект).

Размещая useEffect в вашем компоненте, вы сообщаете React, что хотите запустить обратный вызов в качестве эффекта. React запустит эффект после рендеринга и после обновления DOM.

Если вы передаете только обратный вызов - обратный вызов будет выполняться после каждого рендеринга.

При передаче второго аргумента (массива) React будет запускать обратный вызов после первого рендеринга и каждый раз, когда один из элементов в массиве изменяется. например, при размещении useEffect(() => console.log('hello'), [someVar, someOtherVar]) - обратный вызов будет выполняться после первого рендеринга и после любого рендеринга, что один из someVar или someOtherVar изменяется.

Передавая второй аргумент пустой массив, React будет сравнивать после каждого рендеринга массив и будет видеть, что ничего не изменилось, вызывая обратный вызов только после первого рендеринга.

Ответ 3

Передайте пустой массив в качестве второго аргумента useEffect. Это фактически говорит React, цитируя документы:

Это говорит React, что ваш эффект не зависит от каких-либо значений из реквизита или состояния, поэтому его не нужно повторно запускать.

Вот фрагмент, который вы можете запустить, чтобы показать, что он работает:

function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  }, []); // Pass empty array to only run once on mount.
  
  return <div>
    {user ? user.name.first : 'Loading...'}
  </div>;
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

Ответ 4

хук useMountEffect

Запуск функции только один раз после монтирования компонентов является настолько распространенным шаблоном, что он оправдывает свою собственную ловушку, которая скрывает детали реализации.

const useMountEffect = (fun) => useEffect(fun, [])

Используйте его в любом функциональном компоненте.

function MyComponent() {
    useMountEffect(function) // function will run only once after it has mounted. 
    return <div>...</div>;
}

О хуке useMountEffect

При использовании useEffect со вторым аргументом массива React запускает обратный вызов после монтирования (начального рендеринга) и после изменения значений в массиве. Поскольку мы передаем пустой массив, он будет работать только после монтирования.

Ответ 5

Так что, если ваш эффект требует зависимости, и вы все еще хотите запустить его только один раз?

useEffect(() => {
    dispatch({
        type: 'get',
        route: 'todos'
    })
}, [dispatch])

Когда я Line 14: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array рассылку, мой браузер сообщает мне следующее: Line 14: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array Line 14: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array

Ответ 6

Мне нравится определять функцию mount, она обманывает EsLint так же, как useMount, и я нахожу ее более понятной.

const mount = () => {
  console.log('mounted')
  // ...

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])