Использование пользовательских хуков React для реализации повторного использования экземпляров бизнес-логики

React.js

Введение

В дополнение к управлению состоянием и жизненным циклом функциональных компонентов, React Hooks может извлекать код общей логической обработки для использования различными компонентами представления.HOCа такжеRender Propsв основном то же самое. В этой статье в качестве примера будет рассмотрен запрос удаленных данных API, и, наконец, будет извлечен настраиваемый хук для общих запросов данных, чтобы разные компоненты могли совместно использовать набор логики, когда им нужно получить данные.

Применимые считыватели

Предполагаемые читатели этой статьи:

  • Иметь основы React
  • К пониманию useEffect а такжеuseState как использовать
  • любопытное сердце

Пример запроса данных

Нажмите кнопку ниже, чтобы просмотреть полный код и рабочий пример этой статьи:


Более полный хук useRequest может относиться к моему проекту с открытым исходным кодом:

нужно

Предположим, у нас есть компонент, которому необходимо одновременно загрузить список сообщений (сообщений в блогах) и задач (todos), каждый из которых отображает 5 заголовков и отображает их в соответствии с состоянием загрузки.loadingкомпоненты. Сначала мы рассмотрим, как использовать общие хуки.

Определить состояния

Сначала определим необходимые состояния:

  // PostsAndTodos.js
  const [posts, setPosts] = useState([]);
  const [isPostsLoading, setIsPostsLoading] = useState();
  const [todos, setTodos] = useState([]);
  const [isTodosLoading, setIsTodosLoading] = useState();
  • postsИспользуется для сохранения удаленно загруженных данных статьи,isPostsLoadingСохраните состояние загрузки данных статьи
  • todosсохранить задачи,isTodosLoadingLoad - сделайте данные состояния
  • Здесь мы определяем 4 подобных состояния

Загрузить сообщения

Мы используемuseEffectзапроситьpostsданные:

  // PostsAndTodos.js
  useEffect(() => {
    const loadPosts = async () => {
      setIsPostsLoading(true);
      try {
        let response = await fetch(
          "https://jsonplaceholder.typicode.com/posts?_limit=5"
        );
        let data = await response.json();
        setPosts(data);
      } catch (e) {
        console.log(e);
      }

      setIsPostsLoading(false);
    };
    loadPosts();
  }, []);
  1. мы первыеuseEffectДля того, чтобы определитьloadPosts()Функция запроса удаленных данных.
  2. существуетloadPosts()В функции в начале запроса мы устанавливаем состояние загрузки вtrue, в конце, с ошибками или без, установите состояние загрузки вfalse.
  3. Затем в середине функции мы инициируем запрос на обновление возвращаемых данных доstateсередина.

Загрузить задачи

нагрузкаtodosкод и загрузкаpostsКод в основном такой же, за исключением того, что запрошенныйurlВ отличие от имени функции, которая обновляет состояние:

  // PostsAndTodos.js
  useEffect(() => {
    const loadTodos = async () => {
      setIsTodosLoading(true);
      try {
        let response = await fetch(
          "https://jsonplaceholder.typicode.com/todos?_limit=5"
        );
        let data = await response.json();
        setTodos(data);
      } catch (e) {
        console.log(e);
      } finally {
        setIsTodosLoading(false);
      }
    };
    loadTodos();
  }, []);

Данные дисплея

Здесь мы просто используем<ul>а также<li>экспонатpostsа такжеtodosсписок:

<div>
  <h1>Posts</h1>
  <ul>
    {isPostsLoading ? (
      <div>loading...</div>
    ) : (
      posts.map(post => <li key={post.id}>{post.title}</li>)
    )}
  </ul>
  <h1>Todos</h1>
  <ul>
    {isTodosLoading ? (
      <div>loading...</div>
    ) : (
      todos.map(todo => <li key={todo.id}>{todo.title}</li>)
    )}
  </ul>
</div>

вопрос

Проблема с этим очевидна, мы по сути дважды повторили логику запроса данных, разница только в запросеurl, это не соответствует спецификации дизайна многократно используемого кода, и этот компонент переполнен большим количеством кода бизнес-логики и логики отображения, который очень раздут и нарушает принцип проектирования разделения компонентов, то есть компонент, отвечающий за отображение должны быть отделены от кода для бизнес-обработки. Так как его оптимизировать?

Модернизация с пользовательскими крючками

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

useRequest

мы создаем новыйuseRequest.jsфайл, затем определите функцию с тем же именем:

function useRequest(url) {
   // 代码
   
   // return ...
}

React официально рекомендует использовать пользовательские хукиuseв начале, потому что такое соглашение об именах позволяет человеку, читающему код, знать, что с ним делать. Эта функция имеет следующие характеристики:

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

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

Копируем и загружаемpostsилиtodosкод дляuseRequestфункция, а затем немного модифицированная:

  // useRequest.js
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState();
  const [error, setError] = useState();

  useEffect(() => {
    const loadData = async () => {
      setIsLoading(true);
      try {
        let response = await fetch(url);
        let data = await response.json();
        setData(data);
      } catch (e) {
        setError(e);
      } finally {
        setIsLoading(false);
      }
    };
    loadData();
  }, []);

  return [data, isLoading, error];

Здесь мы изменили наименование состояния и данных на что-то более общее.data, isLoading, а такжеerror, поскольку мы не ограничиваем конкретный домен, любой компонент, которому необходимо загрузить данные, может использовать этот хук. В конце функции возвращаемdata, isLoading, errorЭти три состояния используются компонентом, который вызывает этот хук.Требований к типу возвращаемого значения нет.Если возвращаемых значений много, может быть возвращен и объект, например:

  return {data, isLoading, error}

Загрузить сообщения и задачи

Теперь давайте посмотрим на код, который снова загружает данные, в новомPostsAndTodosWithHooks.jsфайл, нам нужно только вызвать компонентuseRequestхук для получения данных:

// PostsAndTodosWithHooks.js
const [posts, isPostsLoading] = useRequest(
    "https://jsonplaceholder.typicode.com/posts?_limit=5"
);
const [todos, isTodosLoading] = useRequest(
    "https://jsonplaceholder.typicode.com/todos?_limit=5"
);

  • useRequestкрючок нуждается вurlПараметр, адрес запрашиваемых данных.
  • Мы получаем возвращенные данные и возвращаемое значение состояния загрузки, используем синтаксис ES6 и помещаемdataа такжеisLoadingпсевдоним.
  • когдаuseRequestКрюкdataа такжеisLoadingПри обновленииPostsAndTodosЗначения, используемые в этих двух состояниях в компоненте, также обновляются синхронно.
  • Код в части рендеринга компонента остается прежним.

При запуске кода эффект тот же, что и раньше, но код намного проще, хуки и компоненты отображения выполняют свои обязанности, делая код бизнес-логики и представлений понятным и легко читаемым. Более того, повторное использование бизнес-логики также значительно сокращает объем кода, который в данном случае напрямую сокращается на 50%.

государственная изоляция

Некоторым может быть любопытно, не будут ли конфликтовать два вызова одного и того же состояния Hook? еслиpostsПосле загрузки сначала, затемtodosБудет ли он перезаписан после завершения загрузкиpostsДанные? Ответ - нет. Все состояния различных вызовов хуков изолированы, даже если один и тот же хук вызывается несколько раз, они сохраняют свое собственное состояние и не влияют друг на друга. Когда состояние в хуке обновляется, оно синхронизируется с компонентом, который его вызвал.

расширять

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

Суммировать

Пользовательские хуки React предоставляют чрезвычайно важные функции, которые позволяют отделить бизнес-логику от компонентов отображения и использовать ее многократно, что значительно сокращает объем кода и повышает эффективность. Его освоение очень поможет фронтенд-разработке, вот обзор его возможностей и мер предосторожности:

  • Пользовательский крючокuseначало
  • может принимать параметры
  • Может возвращать любой тип возвращаемого значения
  • Можно использовать другие встроенные или настраиваемые хуки.
  • Каждый раз, когда вызывается хук, состояние и логика в нем изолируются.

Вы научились этому? Если у вас есть какие-либо вопросы, пожалуйста, оставьте сообщение.