Як викликати функцію завантаження за допомогою React useEffect лише один раз


210

UseEffect Реагувати гачок буде працювати переданий в функції при кожній зміні. Це можна оптимізувати, щоб дозволити йому дзвонити лише тоді, коли потрібні властивості змінюються.

Що робити, якщо я хочу викликати функцію ініціалізації componentDidMountта не викликати її знову при змінах? Скажімо, я хочу завантажити об'єкт, але функція завантаження не потребує даних із компонента. Як ми можемо це зробити за допомогою useEffectгачка?

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

З гачками це може виглядати приблизно так:

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

Відповіді:


396

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

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

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

27
Крім того, якщо є парами, які ви використовуєте для отримання даних (наприклад, ідентифікатор користувача), ви можете передати ідентифікатор користувача в цей масив, і якщо він зміниться, компонент перезавантажить дані. Багато випадків використання будуть працювати так.
trixn

4
так ... докладніше про пропуску задокументовано тут: reactjs.org/docs/…
Melounek

Це здається найпростішою відповіддю, але ESLint скаржиться ... дивіться іншу відповідь на цю тему stackoverflow.com/a/56767883/1550587
Саймон Хатчісон

Просто передайте loadDataOnlyOnce в масив залежностей. Це працює?
jpmarks

88

TL; DR

useEffect(yourCallback, []) - викликає зворотний виклик лише після першого візуалізації.

Детальне пояснення

useEffectзапускається за замовчуванням після кожного візуалізації компонента (тим самим викликаючи ефект).

Розміщуючи useEffectсвій компонент, ви скажете React, що хочете запустити зворотний дзвінок як ефект. React запустить ефект після надання та після оновлення DOM.

Якщо ви передаєте лише зворотний дзвінок - зворотний дзвінок запускається після кожного рендерінгу.

Якщо передається другий аргумент (масив), React запустить зворотний виклик після першого візуалізації та кожного разу, коли один із елементів масиву буде змінено. наприклад, при розміщенні useEffect(() => console.log('hello'), [someVar, someOtherVar])- зворотний виклик буде виконуватися після першого візуалізації та після будь-якого візуалізації, що один із someVarабо someOtherVarзмінений.

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


68

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 запустить зворотний виклик після монтажу (початкове візуалізація) та після зміни значень масиву. Оскільки ми передаємо порожній масив, він запуститься лише після монтажу.


19
Я дуже віддаю перевагу вашій відповіді, оскільки правило ESLint "реагувати-гачки / вичерпні депа" завжди буде невдалим у порожніх списках залежностей. Наприклад, відомий шаблон create-react-app шаблон буде виконувати це правило.
Диналон

1
Цілком погоджуюся з @Dynalon. Це має бути прийнятим рішенням, оскільки воно не заважає правилу
ESLint

Дякую @Dynalon та Mikado68 :-). Рішення - привілей ОП. Мені було повідомлено про ваші коментарі, але ОП не. Ви можете запропонувати його, коментуючи безпосередньо це питання.
Бен Карп

2
Тепер ви можете використовувати, useMountколи для вашої функції ефекту потрібно щось із реквізиту, але ніколи не потрібно запускати його знову, навіть якщо це значення змінюється без попередження лінійки: useEffect(()=>console.log(props.val),[])буде відсутнє попередження про залежність, але useMount(()=>console.log(props.val))не спричинить попередження, але "працює". Я не впевнений, чи не буде проблеми з одночасним режимом.
HMR

1
Мені це подобається :) З тієї ж причини, що і раніше; правило ESLint не поскаржиться на це, плюс його ім’я легше зрозуміти, ніж порожній масив
Frexuz

20

Передайте порожній масив як другий аргумент 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/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

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


4

Мені подобається визначати mountфункцію, вона хитрість EsLint так само, useMountяк і я вважаю її більш зрозумілою.

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

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

-1

Хитрість полягає в тому, що useEffect приймає другий параметр.

Другий парам - це масив змінних, який компонент перевірить, щоб переконатися, що він змінився перед повторним рендерингам. Ви можете поставити будь-які шматочки реквізиту і заявити, що хочете, щоб сюди не було.

Або нічого не ставити:

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {

    // Run! Like go get some data from an API.

  }, []); //Empty array as second argument

  return (
    <div>
      {/* Do something with data. */}
    </div>
  );
}

Це забезпечить виконання UseEffect лише один раз.

Примітка з документів:

Якщо ви використовуєте цю оптимізацію, переконайтеся, що масив містить усі значення з області компонента (наприклад, реквізит і стан), які змінюються з часом і використовуються ефектом. В іншому випадку ваш код буде посилатися на несвіжі значення попередніх рендерів.


3
Якщо ви буквально копіюєте / вставляєте з CSS Tricks, найменше, що ви могли б зробити, це зарахувати джерело. css-tricks.com/run-useeffect-only-once
буртиш
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.