вернуться назад

Управление состоянием в React с использованием Valtio

Habr.ru
Быков Станислав
Старший разработчик

Когда дело доходит до управления состоянием в React-приложениях, может возникнуть сложность с выбором подходящей библиотеки. Есть довольно много решений, и каждое со своими особенностями и преимуществами. В таком многообразии выбрать оптимальный вариант становится настоящим вызовом. Меня зовут Станислав Быков, и в этой статье я расскажу про Valtio — простое, но мощное решение для управления состоянием в React.

Valtio предлагает минималистичный и гибкий подход к управлению состоянием, который легко освоить и применить во многих проектах. Более того, Valtio занимает 8-е место среди библиотек управления состоянием по количеству скачиваний в месяц (npm) и 10-е место по количеству звезд на GitHub (превосходя даже некоторые популярные библиотеки, такие как Effector).

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

Преимущества Valtio перед другими инструментами управления состоянием

При выборе инструмента для управления состоянием в React разработчикам приходится сталкиваться с многообразием библиотек и подходов. Valtio выделяется среди них своими преимуществами, которые делают его мощным и привлекательным выбором. Чем Valtio отличается от других библиотек?

  • Простота использования: Valtio предлагает простой и интуитивно понятный интерфейс для работы с состоянием. Он не требует сложной настройки или изучения новых концепций, что делает его легким в освоении даже для новичков. С простым и понятным кодом, разработчики могут быстро создавать и управлять состоянием своих приложений.
  • Производительность: Valtio обеспечивает эффективное управление состоянием и оптимизацию рендеринга компонентов. Благодаря своей реактивной природе, Valtio обновляет компоненты только при необходимости, минимизируя количество лишних перерисовок. Это позволяет создавать быстрые и отзывчивые приложения даже при работе с большими объемами данных.
  • Гибкость и масштабируемость: Valtio предоставляет гибкую модель управления состоянием, которая позволяет разработчикам создавать сложные иерархии состояний и легко масштабировать их. Благодаря использованию прокси-объектов, Valtio предоставляет контроль над изменением состояния и обеспечивает удобный доступ к нужным данным.
  • Интеграция с React и другими инструментами: Valtio без проблем интегрируется с React и другими популярными инструментами в экосистеме JavaScript. Вы можете использовать Valtio вместе с React Hooks, контекстом, React-suspense и другими для создания современных и мощных приложений. Кроме того, Valtio совместим с Redux DevTools, что обеспечивает удобное отслеживание и отладку состояния приложения.
  • Активное сообщество и поддержка: Valtio имеет большое сообщество разработчиков, которые активно используют и поддерживают эту библиотеку. Вы можете найти обширную документацию, примеры использования и помощь в сообществе, что делает процесс изучения и применения Valtio более комфортным.

Основные концепции Valtio

Уникальность Valtio заключается в его мутации состояния. С ним вам не нужно использовать традиционные методы, такие как селекторы и сравнение изменений, для идентификации и перерисовки компонентов на основе состояния.

Библиотека предоставляет набор функций и возможностей, которые упрощают создание прокси-хранилища:

  • proxy — для того, чтобы отслеживать изменения исходного состояния и всех его вложенных объектов, уведомляя слушателей об изменении, его необходимо обернуть в proxy:
import { proxy } from 'valtio';

const state = proxy({ count: 0, text: 'hello' });

Возвращаемый объект state является мутабельным, его можно изменять как обычный объект JavaScript:

setInterval(() => {
++state.count
}, 1000);
  • snapshot — принимает state, обернутый в proxy и возвращает его неизменяемую копию. Это полезно, когда вы хотите передать текущее состояние вашего приложения компоненту или функции, не позволяя ему напрямую изменять состояние. Неизменяемость достигается за счет эффективного глубокого копирования и «замораживания» объекта, что позволяет выполнять поверхностное сравнение функций рендеринга, предотвращая ложный ре-рендеринг.
import { proxy, snapshot } from 'valtio';

const state = proxy({ count: 0, text: 'hello' });
const snap1 = snapshot(store);
const snap2 = snapshot(store);
console.log(snap1 === snap2); // true, нет необходимости делать ре-рендер
  • useSnapshot — при его использовании мы получаем неизменяемую копию состояния, которую можно использовать для чтения данных без возможности изменения исходного состояния. Преимущество использования useSnapshot в том, что мы можем получить текущее состояние приложения в любой момент времени без необходимости слежения за изменениями и без необходимости создания реактивной связи. Это упрощает чтение данных из состояния и позволяет нам создавать компоненты, которые полагаются только на снимок (snapshot) для отображения определенных данных, не вызывая повторные рендеры при изменении остального состояния.
// Ре-рендер будет происходить только при изменении `state.count`, изменения в `state.text` не будут на это влиять
function Counter() {
const snap = useSnapshot(state);
return (
<div>
{snap.count}
<button onClick={() => ++state.count}>+1</button>
</div>
);
};
  • useProxy — утилита работает аналогично методу useSnapshot, но возвращает неглубокое текущее состояние и его снимок. Таким образом, вы можете его изменять, но только на корневом уровне и в пределах области видимости компонента.
import { proxy } from 'valtio';
import { useProxy } from 'valtio/utils';

const state = proxy({ count: 1 });

const Component = () => {
const $state = useProxy(state);
return (
<div>
{$state.count}
<button onClick={() => ++$state.count}>+1</button>
</div>
);
};
  • subscribe — метод, позволяющий получить доступ к состоянию и подписаться на его изменение за пределами компонента. При вызове вы передаете функцию-обработчик, которая будет вызываться каждый раз, когда происходят изменения в состоянии. Также при вызове возвращается метод отписки (unsubscribe), который уместно использовать при размонтировании компонента или другом случае, когда пропадает необходимость следить за изменениями.
import { subscribe } from 'valtio';

// Подписка на все изменения состояния
const unsubscribe = subscribe(state, () =>
// Действия со state
);

// вызов метода отписки
unsubscribe();

Подписаться на изменения можно не только на всё состояние, но и на часть его:

const state = proxy({ obj: { foo: 'bar' }, arr: ['hello'] });

subscribe(state.obj, () => console.log('state.obj has changed to ', state.obj));
state.obj.foo = 'baz';

subscribe(state.arr, () => console.log('state.arr has changed to ', state.arr));
state.arr.push('world');

Похожим функционалом обладают встроенные утилиты subscribeKey и watch:

subscribeKey предназначена для подписки на изменения за примитивным значением состояния:

import { subscribeKey } from 'valtio/utils';

const state = proxy({ count: 0, text: 'hello' });
subscribeKey(state, 'count', (value) =>
// Действия с value
);
  • watch осуществляет подписку через геттеры. В отличии от subscribe предоставляет возможность подписки сразу на несколько прокси-объектов (объектов состояний). Подписка происходит с помощью функции get, переданной в коллбэк (callback). Любые изменения в прокси-объектах проведут к немедленному вызову коллбэка. Следует учесть, что при инициации watch коллбэк будет запущен автоматически, несмотря на то, что изменений в состоянии еще нет, это необходимо для установления начальных подписок.
import { proxy } from 'valtio';
import { watch } from 'valtio/utils';

const userState = proxy({ user: { name: 'Ivan' } });
const sessionState = proxy({ expired: false });

watch((get) => {
// `get` добавляет `sessionState` в прослушиваемые объекты
get(sessionState);
const expired = sessionState.expired;
// или сразу использует их
const name = get(userState).user.name;
console.log(`${name}'s session is ${expired ? 'expired' : 'valid'}`);
});
// 'Ivan's session is valid'
sessionState.expired = true;
// 'Ivan's session is expired'

Метод watch при запуске возвращает функцию очистки, которая может срабатывать автоматически (при каждом повторном вызове коллбэка), либо при её непосредственном вызове. Этому методу очистки можно добавить дополнительную функциональность через return в утилите.

  • Valtio поддерживает React-suspense и будет выдавать промисы, к которым вы обращаетесь внутри функции рендера компонента. Это устраняет необходимость в асинхронных операциях, поэтому вы можете получать доступ к данным напрямую, в то время как родительский компонент отвечает за состояние загрузки и обработку ошибок.
const state = proxy({ post: fetch(url).then((res) => res.json()) });

function Post() {
const snap = useSnapshot(state);
return <div>{snap.post.title}</div>;
};

function App() {
return (
<Suspense fallback="Loading...">
<Post />
</Suspense>
);
};
  • ref — позволяет хранить объекты в состоянии без их отслеживания. Это может быть полезным, если у вас есть большие вложенные объекты с аксессорами, которые вы не хотите проксировать. ref позволяет вам хранить эти объекты внутри модели состояния. Они не будут обернуты во внутренний прокси и, следовательно, не будут отслеживаться.
import { proxy, ref } from 'valtio';

const state = proxy({
count: 0,
dom: ref(document.body),
});
  • Извлекать данные из состояния можно не только с помощью методов и утилит, предоставляемых библиотекой, но и обычной деструктуризацией объекта состояния. В этом случае не будет прокси-обертки и подписки на изменение состояния. Вы можете считывать данные в компоненте без вызова повторного рендера.
function Foo() {
const { count, text } = state;
// ...

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

Итог

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

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

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

А Вы какой библиотекой для управления состоянием предпочитаете пользоваться?

Loading

Мы обрабатываем файлы cookie для корректной работы сайта и персонализации сервисов, в т.ч. с использованием метрических программ и систем аналитик. Продолжая пользоваться сайтом, Вы соглашаетесь с использованием файлов cookie. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, ограничьте использование файлов cookie в настройках браузера. Более подробная информация на странице Политика конфиденциальности.
Принять
Мы обрабатываем файлы cookie для корректной работы сайта и персонализации сервисов, в т.ч. с использованием метрических программ и систем аналитик. Продолжая пользоваться сайтом, Вы соглашаетесь с использованием файлов cookie. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, ограничьте использование файлов cookie в настройках браузера. Более подробная информация на странице Политика конфиденциальности.
Принять