Хуки и переиспользуемая логика

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

createSignal

Основной механизм управления состоянием в Solid.js — это сигналы. Сигналы — это базовый способ хранения и работы с состоянием. Чтобы создать сигнал, используется хук createSignal.

Пример создания сигнала:

const [state, setState] = createSignal(0);

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

Реактивные зависимости

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

Пример использования реактивных зависимостей:

const [count, setCount] = createSignal(0);
const doubledCount = () => count() * 2;

setCount(5); // вызовет обновление для всех зависимых значений
console.log(doubledCount()); // 10

В этом примере переменная doubledCount автоматически обновляется всякий раз, когда меняется сигнал count.

createEffect

Хук createEffect используется для выполнения побочных эффектов, которые зависят от реактивных значений. Когда одно из значений, от которых зависит эффект, изменяется, эффект будет автоматически выполнен.

Пример:

const [count, setCount] = createSignal(0);

createEffect(() => {
  console.log("Текущее значение: ", count());
});

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

createMemo

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

Пример использования createMemo:

const [first, setFirst] = createSignal(2);
const [second, setSecond] = createSignal(3);

const sum = createMemo(() => first() + second());

sum будет пересчитываться только в случае изменений first или second.

Переиспользуемая логика с хуками

В Solid.js можно легко создавать переиспользуемую логику с использованием хуков. Эта возможность особенно полезна для разделения сложных компонентов на меньшие, более понятные и многократно используемые части.

Создание кастомных хуков

Одним из ключевых преимуществ Solid.js является возможность создания кастомных хуков для разделения логики. Это позволяет инкапсулировать состояние и эффекты, делая компоненты более чистыми и простыми для понимания.

Пример кастомного хука для отслеживания таймера:

import { createSignal, createEffect } from 'solid-js';

function useTimer(initialTime = 0) {
  const [time, setTime] = createSignal(initialTime);

  createEffect(() => {
    const interval = setInterval(() => {
      setTime(prev => prev + 1);
    }, 1000);

    return () => clearInterval(interval); // Очистка эффекта
  });

  return time;
}

export default useTimer;

Теперь этот хук можно использовать в любом компоненте:

import useTimer from './useTimer';

function Timer() {
  const time = useTimer();

  return <div>{time()}</div>;
}

В этом примере логика отслеживания времени была вынесена в отдельный хук useTimer, который может быть использован в разных компонентах без дублирования кода.

Работа с контекстами

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

Пример создания и использования контекста:

import { createContext, useContext } from 'solid-js';

const TimerContext = createContext();

function TimerProvider(props) {
  const timer = useTimer(0); // используем кастомный хук

  return (
    <TimerContext.Provider value={timer}>
      {props.children}
    </TimerContext.Provider>
  );
}

function TimerDisplay() {
  const timer = useContext(TimerContext);

  return <div>Текущее время: {timer()}</div>;
}

function App() {
  return (
    <TimerProvider>
      <TimerDisplay />
    </TimerProvider>
  );
}

Здесь TimerProvider предоставляет значение контекста, которое затем может быть использовано в дочерних компонентах через useContext. Это позволяет организовать глобальное состояние в приложении, не передавая его через пропсы.

Хуки для работы с формами

В Solid.js создание кастомных хуков для работы с формами позволяет эффективно управлять состоянием инпутов и их валидацией. Например, можно создать хук для обработки ввода в текстовое поле:

import { createSignal } from 'solid-js';

function useInput(initialValue = '') {
  const [value, setValue] = createSignal(initialValue);

  function handleChange(event) {
    setValue(event.target.value);
  }

  return [value, handleChange];
}

export default useInput;

Этот хук можно использовать для всех инпутов в приложении, таким образом централизуя логику обработки ввода.

Использование хуков в жизненных циклах компонентов

Solid.js предоставляет хуки, которые позволяют работать с жизненным циклом компонентов, например, onCleanup для очистки эффектов. Это полезно, когда нужно контролировать ресурсы, например, подписки на события или таймеры.

Пример использования onCleanup:

import { createSignal, onCleanup } from 'solid-js';

function TimerComponent() {
  const [time, setTime] = createSignal(0);

  const interval = setInterval(() => {
    setTime(prev => prev + 1);
  }, 1000);

  onCleanup(() => clearInterval(interval)); // Очистка интервала при размонтировании компонента

  return <div>{time()}</div>;
}

onCleanup автоматически вызовется, когда компонент будет удалён из DOM, и интервал будет очищен, предотвращая утечку памяти.

Итоги

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