Обзор

Откройте для себя скрытый источник энергии в экосистеме React: хук «useSyncExternalStore». В этой статье рассматривается его преобразующий потенциал, бросающий вызов традиционным парадигмам государственного управления. Бесшовно интегрируя внешние источники данных и улучшая взаимодействие между компонентами, этот хук предлагает нетрадиционный, но мощный подход.

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

Применение

Согласно React, useSyncExternalStore — это React Hook, который позволяет вам подписаться на внешний магазин. Но что такое «внешний магазин»? Он буквально выполняет 2 функции:

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

Ладно, поначалу может быть трудно получить. Мы можем перейти к примеру.

Демо

Для нашей сегодняшней демонстрации я зайду в классическое приложение: «Список дел».

Магазин

Во-первых, мы должны определить начальное состояние:

export type Task = {
  id: string;
  content: string;
  isDone: boolean;
};

export type InitialState = {
  todos: Task[];
};

export const initialState: InitialState = { todos: [] };

Вы можете видеть, что я определил типы, а затем создал состояние, в котором todos является пустым массивом.

Теперь редуктор:

export function reducer(state: InitialState, action: any) {
  switch (action.type) {
    case "ADD_TASK":
      const task = {
        content: action.payload,
        id: uid(),
        isDone: false,
      };
      return {
        ...state,
        todos: [...state.todos, task],
      };

    case "REMOVE_TASK":
      return {
        ...state,
        todos: state.todos.filter((task) => task.id !== action.payload),
      };

    case "COMPLETE_TASK":
      const tasks = state.todos.map((task) => {
        if (task.id === action.payload) {
          task.isDone = !task.isDone;
        }
        return task;
      });
      return {
        ...state,
        todos: tasks,
      };

    default:
      return state;
  }

У нашего редуктора всего 3 действия: ADD_TASK, REMOVE_TASK и COMPLETE_TASK. Это классический пример логики списка дел.

Наконец то, что мы ждем, магазин:

let listeners: any[] = [];

function createStore(reducer: any, initialState: InitialState) {
  let state = initialState;

  function getState() {
    return state;
  }

  function dispatch(action: any) {
    state = reducer(state, action);

    emitChange();
  }

  function subscribe(listener: any) {
    listeners = [...listeners, listener];
    return () => {
      listeners = listeners.filter((l) => l !== listener);
    };
  }

  const store = {
    dispatch,
    getState,
    subscribe,
  };

  return store;
}

function emitChange() {
  for (let listener of listeners) {
    listener();
  }
}

export const store = createStore(reducer, initialState);

Этот фрагмент кода иллюстрирует создание простой системы управления состоянием, похожей на Redux, в TypeScript. Вот описание того, как это работает:

  1. listeners Массив: этот массив содержит список функций прослушивателя, которые будут получать уведомления при каждом изменении состояния.
  2. createStore Функция. Эта функция отвечает за создание магазина в стиле Redux. Он принимает два параметра:
  • reducer: функция редуктора, отвечающая за вычисление следующего состояния на основе текущего состояния и отправленного действия.
  • initialState: исходное состояние приложения.

3. state: эта переменная содержит текущее состояние приложения.

4. getState Функция: возвращает текущее состояние.

5. Функция dispatch: принимает объект действия, передает его редьюсеру вместе с текущим состоянием, обновляет состояние с результатом, а затем вызывает функцию emitChange, чтобы уведомить слушателей об изменении состояния.

6. Функцияsubscribe: принимает функцию прослушивателя, добавляет ее в массив listeners и возвращает функцию очистки, которую можно вызвать для удаления прослушивателя.

7. Объектstore: созданный объект хранилища содержит ссылки на функции dispatch, getState и subscribe.

8. Функция emitChange: перебирает массив listeners и вызывает каждую функцию прослушивателя, уведомляя их об изменении состояния.

В конце кода с помощью функции createStore создается store с заданным редьюсером и начальным состоянием. Это хранилище теперь можно импортировать и использовать в других частях приложения для управления и контроля состояния.

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

Чтобы использовать крючок useSyncExternalStore. Мы можем получить состояние следующим образом:

const { todos } = useSyncExternalStore(store.subscribe, store.getState);

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

За и против

Хук «useSyncExternalStore» имеет как преимущества, так и потенциальные недостатки в контексте управления состоянием в приложении React:

Плюсы:

  1. Простая интеграция с внешними источниками. Этот хук обеспечивает простую интеграцию с внешними источниками данных, продвигая единый подход к управлению состоянием. Эта интеграция может упростить обработку данных из различных источников, повышая целостность приложения.
  2. Межкомпонентная связь. «useSyncExternalStore» обеспечивает эффективную связь между компонентами, оптимизируя обмен данными и снижая потребность в сложном детализации реквизитов или управлении контекстом.
  3. Улучшение производительности. Благодаря централизации управления состоянием и минимизации распространения обновлений состояния этот хук может оптимизировать производительность рендеринга, что приводит к более быстрому отклику и эффективности приложения.
  4. Простота и чистый код: абстрактный API хука может привести к более чистому и организованному коду, упрощая его понимание и поддержку, особенно в крупномасштабных приложениях.
  5. Сокращенный шаблон: «useSyncExternalStore» может уменьшить потребность в написании избыточного кода для управления состоянием, предоставляя краткий и последовательный способ управления состоянием всего приложения.

Минусы:

  1. Кривая обучения. Разработчики, не знакомые с этим крючком, могут столкнуться с кривой обучения при переходе с более устоявшихся решений для управления состоянием. Адаптация к новому подходу может изначально замедлить разработку.
  2. Ограничения настройки. Предопределенные функции хука могут не полностью соответствовать уникальным требованиям каждого приложения. Настройка поведения за пределами возможностей хука может потребовать дополнительных обходных путей.
  3. Потенциальные накладные расходы на абстракцию. В зависимости от своей внутренней механики хук может привести к небольшому увеличению производительности или использования памяти по сравнению с более оптимизированными решениями, разработанными специально для нужд приложения.
  4. Сообщество и экосистема. В качестве недооцененного или малоизвестного хука «useSyncExternalStore» может отсутствовать устоявшееся сообщество и комплексная экосистема, что может привести к уменьшению количества доступных ресурсов или сторонних библиотек.
  5. Совместимость и будущие обновления. Совместимость с будущими версиями React и потенциальные обновления самого хука могут вызывать беспокойство. Обеспечение долгосрочной поддержки и беспрепятственного обновления может потребовать особого внимания.

Заключение

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

Исходный код: https://github.com/superdev163/useSyncExternalStore