Вы так долго использовали хук React useEffect, правильно ли вы его используете?

React.js

useEffect — это ядро ​​React Hooks, необходимо убедиться, что вы понимаете его механизм работы и правильное использование, чтобы избежать таких ям. В моей предыдущей работе из-за этого я столкнулся с бесчисленными ямами, такими как полученное значение устарело, оно не выполняется, когда оно должно выполняться, и выполняется, когда оно не должно выполняться...

Итак, чтобы избежать вышеуказанного смущения, сэкономить время на то, чтобы ломать голову в поисках ошибок, и защитить нашу линию роста волос, мы должны серьезно научиться правильно использовать хук useEffect.

что такое эффект

Как говорится, если ты знаешь врага и знаешь врага, ты будешь в опасности в сотне битв.Давайте сначала поймем, что волшебный конь - это Следствие. На самом деле, все в той или иной степени в процессе разработки сталкивались с концепцией побочных эффектов, то есть

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

Отношение к жизненному циклу

Статьи на рынке, включая официальные документы, позволяют намuseEffectпредставлять себеcomponentDidMount,componentDidUpdate,componentWillUnmountСочетание трех жизненных циклов. Не совсем, если вам нужноuseEffectМеханизм работы зависит от жизненного цикла, что вызовет некоторую логическую путаницу, а затем вызовет ошибки. Все, что нам нужно сделать, это положитьuseEffectКак совершенно новая функция, она посвящена функциональным компонентам, так что вы не будете путаться при ее использовании. Ниже мы демонстрируем его различные варианты использования на примерах.

Продолжительность

useEffect Он должен выполняться один раз во время рендеринга., остальные тайминги зависят от следующих условий:

  • Второго параметра нет.useEffectХук принимает два параметра, первый — это код, который нужно выполнить, а второй — массив, задающий набор зависимых переменных, при изменении любой из которых эффект будет выполняться повторно.
  • Нет возвращаемого значения.useEffectФункция может быть возвращена в коде выполнения .Эта функция будет выполняться перед каждым новым рендерингом или когда компонент размонтируется для очистки.

Давайте сначала рассмотрим простой пример. Если вы хотите увидеть полный код и поиграть с ним, нажмите кнопку ниже.


Сначала посмотрим на верх<App />код:

function App() {
  const [showList, setShowList] = useState(false);
  const [postCount, setPostCount] = useState(5);

  return (
    <div className="App">
      <button onClick={() => setShowList(!showList)}>
        {showList ? "隐藏" : "显示"}
      </button>
      <button onClick={() => setPostCount(previousCount => previousCount + 1)}>
        增加数量
      </button>
      {showList && <PostList count={postCount} />}
    </div>
  );
}

Этот компонент используется для отображения списка статей, а также кнопок для управления отображением списка статей и кнопок для управления количеством отображаемых статей. мы используемshowListгосударство контролировать<PostList />отображать или нет. это сделать<PostList />Компонент размонтируется, а затем рендерится, чтобы доказать, что каждый раз, когда он рендерится и размонтируется,useEffectхук запустится один раз.<PostList />Код компонента выглядит следующим образом:

function PostList({ count = 5 }) {
  useEffect(() => {
    let p = document.createElement("p");
    p.innerHTML = `当前文章数量:${count}`;
    document.body.append(p);
  });

  return (
    <ul>
      {new Array(count).fill("文章标题").map((value, index) => {
        return (
          <li key={index}>
            {value}
            {index + 1}
          </li>
        );
      })}
    </ul>
  );
}

Этот компонент показывает<ul>Список ради простоты генерирует несколько скучных заголовков статей. Давайте сосредоточимся наuseEffectПроделанные действия:

  • Создаватьpэлемент
  • настраиватьpТекст - это номер текущей статьи
  • добавитьpприбытьbodyпоследний

Здесь эффект не возвращает никакого значения и не передает ему ни одного параметра, что это будет за эффект?
没返回值

Ответ заключается в том, что этот эффект будетcountилиshowListкаждый щелчок при изменении显示или增加数量кнопка, наша недавно добавленнаяpбудет добавлено снова.Это также яма, которая вызывает утечку контента, если мы добавим сюда слишком много вещей, потребляющих память, без очистки, браузер не заставит себя долго ждать ~ Решение очень простое, дайтеuseEffectДобавьте возвращаемое значение и внутри него удалите то, которое мы добавляемpэлемент может быть:

useEffect(() => {
  let p = document.createElement("p");
  p.innerHTML = `当前文章数量:${count}`;
  document.body.append(p);

  return () => {
    p.remove();
  };
});

Таким образом, когда мы нажимаем кнопку, мы гарантируем, что есть только одинpна текущей странице. Видишь, не лучше ли так писать, чем раскидыватьcomponentDidMountа такжеcomponentWillUnmountЭто удобнее? Мы можем легко получить его в том же объемеp, просто удалите его.

有返回值

Пример класса в действии — очистка данных

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

Edit async loading

В этом примере<PostList />Код компонента был немного изменен, сначала мы определяем два новых состояния:

  • сообщения. Сохранить список статей, загруженных удаленно
  • загрузка. Журнал статуса запроса ajax
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);

Сначала мы думалиfetchявляется асинхронной операцией, то вы должны датьuseEffectПередаёт ли хук асинхронную функцию? Неправильно, функция, переданная в useEffect, не может быть асинхронной, потому что суть асинхронности в том, чтобы вернуть промис, аuseEffectЕдинственным возвращаемым значением является функция. Используя async, я получаю следующее исключение:

Warning: An effect function must not return anything besides a function, which is used for clean-up.

// 错误
useEffect(async () => {
  // const response = await fetch("https://jsonplaceholder.typicode.com/posts");
});

Правильный способ его написания — определить логику захвата данных в отдельную функцию, а затемuseEffectназовите это:

useEffect(() => {
  // const response = await fetch("https://jsonplaceholder.typicode.com/posts");

  const loadPosts = async () => {
    setLoading(true);
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/posts?_limit=${count}`
    );
    const data = await response.json();
    setPosts(data);
    setLoading(false);
  };

  loadPosts();
}, [count]);

Что он делает, перед запросом данных помещаетloadingстатус установлен в true, то в соответствии сcountзначение, чтобы получить соответствующее количество списков статей, и обновить возвращаемое значение доpostsсостояние, затем поставитьloadingУстановите значение «ложь». Наконец, согласноloadingсостояние, мы показываем加载中или文章列表:

if (loading) return <div>loading...</div>;

return (
  <ul>
    {posts.slice(0, count).map((post, index) => {
      return <li key={post.id}>{post.title}</li>;
    })}
  </ul>
);

加载数据

второй параметр

В приведенном выше примере мы даемuseEffectпередал второй параметр и поставилcountкак зависимое значение всякий раз, когдаcountПри изменении этот эффект будет повторно выполнен один раз для загрузки новых данных. Кроме того, если мы скроем список и нажмем显示При нажатии кнопки эффект также будет запущен снова, потому что, когда щелчок скрыт,<PostList />Компонент размонтируется, а затем перерисовывается, когда он снова отображается, мы можем в соответствии сloading...Этот знак можно увидеть.

если мыЕсли вы удалите второй параметр, вы попадете в яму бесконечного цикла.,Зачем? Потому что при выполнении эффекта он будет обновлятьсяpostsа такжеloadingЭти два состояния, и когда состояние изменится, компонент снова будет повторно отображаться в соответствии сuseEffectНетрудно сделать выводы из закона, что каждый рендер должен быть выполнен один раз.

то если мыКак насчет того, чтобы дать ему пустой массив? Тогда как бы вы не нажимали на увеличение числа, этот эффект не будет выполнен повторно, в результате чего всегда загружаются только 5 статей по умолчанию.

加载数据-无依赖数组

Добавить другие свойства

Мы можем попробовать добавить еще одно свойство для проверкиuseEffectЗависит от свойств массива. существует<App />В компонент добавляем состояние макетаverticalи кнопки для изменения макета для управления<PostList>Горизонтальное и вертикальное расположение компонентов:

// APP
function App() {
  // 其它代码省略
  const [vertical, setVertical] = useState(true);

  return (
    <div className="App">
      {/* 其它代码省略 */}
      <button onClick={() => setVertical(prev => !prev)}>更改布局</button>
      {showList && <PostList count={postCount} vertical={vertical} />}
    </div>
  );
}

// PostList
function PostList({ count = 5, vertical = false }) {
  // 其它代码省略
  useEffect(() => {
    // const response = await fetch("https://jsonplaceholder.typicode.com/posts");

    const loadPosts = async () => {
      setLoading(true);
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/posts?_limit=${count}`
      );
      const data = await response.json();
      setPosts(data);
      setLoading(false);
    };

    loadPosts();
  }, [count, vertical]); // 在这里添加 vertical 作为依赖

  // 其它代码省略
}

Здесь мы добавляем второй параметрverticalзависимы, так что каждый щелчок更改布局При нажатии кнопки список статей будет загружен один раз, что подходит для ситуации, когда данные нужно повторно запрашивать при изменении макета:
加载数据-依赖layout

Если вам не нужно перезагружать данные, просто поставьтеverticalПросто удалите его из массива зависимостей.

加载数据-不依赖layout

фокус

Посмотрите на часто используемыеuseEffectПравильно ли используется? Чтобы отметить ключевые моменты:

  1. это не совсемcomponentDidMount,componentDidUpdate,componentWillUnmountСочетание трех жизненных циклов (у людей свои представления).
  2. Он будет выполняться один раз при каждом рендеринге.
  3. Если возвращается функция, код, который возвращает функцию, должен быть запущен один раз до следующего рендеринга или до размонтирования компонента.
  4. Если зависимый массив указан и не пуст, он будет перезапущен при изменении каждого элемента в массиве.
  5. Если массив пуст, он будет выполнен только один раз при первом рендеринге, если есть возвращаемое значение, такое же, как 3.
  6. если вuseEffectЕсли состояние обновляется в , а массив зависимостей не указан или состояние существует в массиве зависимостей, это вызовет бесконечный цикл.

Вы все поняли? Если у вас есть какие-либо вопросы, пишите мне в комментарии или в личные сообщения! Если вы считаете, что статья полезна, пожалуйста, подпишитесь на блогера, спасибо, сравните свое сердце.