Back

Лучшие практики работы с SolidJS

Лучшие практики работы с SolidJS

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

Ключевые выводы

  • Компоненты SolidJS выполняются один раз как функции настройки — реактивность существует на уровне сигналов, а не на уровне компонентов
  • Держите чтение сигналов внутри реактивных областей: JSX-выражения, createEffect или createMemo
  • Выводите значения с помощью функций или createMemo вместо синхронизации состояния через эффекты
  • Никогда не деструктурируйте props — используйте mergeProps и splitProps для сохранения цепочки геттеров
  • Используйте createStore для вложенного состояния и createResource для асинхронной загрузки данных

Поймите, что компоненты выполняются один раз

Это изменение ментальной модели, от которого зависит всё остальное. В React компоненты перерисовываются при изменении состояния. В SolidJS компоненты выполняются один раз как функции настройки. Реактивность происходит на уровне сигналов, а не на уровне компонентов.

Это означает, что следующий код неправильный:

function Counter() {
  const [count, setCount] = createSignal(0)
  const doubled = count() * 2 // Выполняется один раз. Никогда не обновляется.
  return <div>{doubled}</div>
}

Исправление заключается в том, чтобы держать чтение сигналов внутри реактивных областей — JSX-выражений, createEffect или createMemo:

function Counter() {
  const [count, setCount] = createSignal(0)
  const doubled = createMemo(() => count() * 2) // Отслеживает count реактивно
  return <div>{doubled()}</div>
}

Паттерны реактивности SolidJS: сигналы, мемо и эффекты

Детальная реактивность в SolidJS построена на трёх примитивах. Знание того, когда использовать каждый из них, является ключевым для написания эффективных компонентов SolidJS.

  • createSignal — для примитивных значений и простого состояния
  • createMemo — для производных значений, зависящих от сигналов
  • createEffect — только для побочных эффектов (манипуляции с DOM, сторонние библиотеки)

Наиболее распространённая ошибка — использование createEffect для синхронизации состояния:

// ❌ Антипаттерн: эффект используется для вывода значения
createEffect(() => setFullName(`${firstName()} ${lastName()}`))

// ✅ Правильно: выводите напрямую
const fullName = () => `${firstName()} ${lastName()}`

Обычная функция вроде fullName работает, потому что SolidJS пересчитывает её всякий раз, когда она появляется внутри реактивной области, которая читает её базовые сигналы. Используйте createMemo только тогда, когда вывод значения является дорогостоящим и вы хотите кешировать результат.

Оставьте createEffect для работы вне реактивного графа Solid — таких вещей, как инициализация библиотеки графиков или императивное обновление элемента DOM. Смотрите официальное объяснение мемо и производных значений для более глубоких деталей.

Никогда не деструктурируйте props

Props в SolidJS поддерживаются геттерами. Их деструктуризация разрывает реактивное соединение:

// ❌ Нарушает реактивность
function User({ name }: { name: string }) {
  return <h1>{name}</h1>
}

// ✅ Сохраняет реактивность
function User(props: { name: string }) {
  return <h1>{props.name}</h1>
}

Когда вам нужны значения по умолчанию или вы хотите разделить props, которые вы используете, от props, которые вы передаёте дальше, используйте mergeProps и splitProps — оба сохраняют цепочку геттеров.

Используйте компоненты управления потоком для условного рендеринга и рендеринга списков

Паттерны производительности SolidJS зависят от использования правильных примитивов рендеринга. Предпочитайте компоненты управления потоком Solid для реактивных условий и списков, а не полагайтесь на чистую логику JavaScript внутри JSX.

// ✅ Условный рендеринг
<Show when={isLoggedIn()} fallback={<LoginPage />}>
  <Dashboard />
</Show>

// ✅ Рендеринг списка — сохраняет идентичность элементов при обновлениях
<For each={posts()}>{(post) => <PostCard post={post} />}</For>

Используйте <Index> вместо <For>, когда позиции в списке остаются стабильными, но значения в этих позициях могут меняться. <For> отслеживает элементы по ссылке и лучше всего работает для массивов объектов со стабильной идентичностью, в то время как <Index> отслеживает элементы по позиции.

Вы можете прочитать больше о примитивах рендеринга списков Solid в официальной документации.

Используйте хранилища для сложного состояния

Сигналы отслеживают изменения одного значения. Когда это значение является большим вложенным объектом, обновления заменяют всё значение целиком, что означает, что все потребители этого сигнала реагируют на изменение. createStore обеспечивает реактивность на уровне свойств, что лучше работает для вложенного состояния:

const [state, setState] = createStore({ user: { name: "Alice" }, posts: [] })

// Только компоненты, читающие state.posts, реагируют на это
setState("posts", (p) => [...p, newPost])

Используйте produce для сложных мутаций и reconcile при слиянии данных с сервера в существующее хранилище.

Правильная работа с асинхронными данными

Загрузка данных внутри createEffect может привести к состояниям гонки и не интегрируется чисто с Suspense. Используйте вместо этого createResource:

const [posts] = createResource(() => fetch("/api/posts").then((r) => r.json()))

createResource принимает необязательный исходный сигнал в качестве первого аргумента. Когда он предоставлен, функция загрузки перезапускается всякий раз, когда этот источник меняется, и ресурс автоматически интегрируется с компонентами <Suspense> и <ErrorBoundary> в Solid.

В приложениях SolidStart используйте query и createAsync вместе с функциями предзагрузки. Функции предзагрузки могут выполняться во время намерения навигации (например, при наведении на ссылку) и снова во время навигации, позволяя данным быть готовыми к моменту рендеринга компонента.

Заключение

SolidJS вознаграждает разработчиков, которые работают с его моделью реактивности, а не против неё. Держите чтение сигналов внутри реактивных областей, выводите значения вместо их синхронизации через эффекты, никогда не деструктурируйте props и используйте createStore, когда состояние имеет вложенную структуру. Эти паттерны реактивности SolidJS — не произвольные правила, они являются прямым следствием того, как работает детальная реактивность, и их соблюдение создаёт компоненты, которые одновременно корректны и быстры.

Часто задаваемые вопросы

Компоненты SolidJS выполняются один раз, поэтому любое чтение сигнала, присвоенное обычной переменной вне реактивной области, захватывает только начальное значение. Оберните вывод в createMemo или поместите чтение сигнала непосредственно внутри JSX. Оба являются реактивными областями, которые пересчитываются всякий раз, когда изменяется базовый сигнал.

Обычная производная функция отлично работает для лёгких вычислений, потому что SolidJS пересчитывает её каждый раз, когда выполняется потребляющая реактивная область. Используйте createMemo, когда вывод является дорогостоящим или читается несколькими потребителями. createMemo кеширует результат и пересчитывает только тогда, когда меняются его зависимости.

For отслеживает каждый элемент по ссылке, поэтому он идеален для массивов объектов со стабильной идентичностью. Index отслеживает элементы по их позиции в массиве, что делает его более подходящим для списков, где позиция остаётся стабильной, но значение в этой позиции может меняться.

Вы можете, но не должны. Использование createEffect для синхронизации производного состояния создаёт ненужные промежуточные обновления и может вызвать глитчи. Вместо этого выводите значение с помощью функции или createMemo. Оставьте createEffect для настоящих побочных эффектов, таких как манипуляции с DOM, логирование или взаимодействие со сторонними библиотеками.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay