Приветствую дамы и господа! В этой статье я расскажу о JavaScript библиотеке Biscuit-store.
Описание
Biscuit - это многофункциональный, гибкий, модульный инструмент для создания и удобной работы с контейнерами управляемых состояний в JavaScript приложениях.
Основные цели статьи
Рассказать о biscuit-store и его целях;
Провести сравнение с другими подобными инструментами;
Дать краткий обзор функционала.
Здесь я не буду погружаться под капот, а лишь проведу краткий обзор.
Плюсы biscuit-store
Стремление к простоте исполнения;
Поддержка React;
Стремление к единому подходу;
Асинхронность из коробки;
Простое расширение через Middleware;
Отсутствие зависимостей;
Гибкая модульная архитектура;
Оптимальное соотношение размера библиотеки и количества функций;
Встроенный отладчик.
Характеристики
Вес core – 18Kb, Gzip: 6.2кб (скомпилировано в CommonJs);
Вес react модуля – 6.8, Gzip: 2.0кб;
Вес adapter модуля – 9.6, Gzip: 3.5кб (скомпилировано в CommonJs);
Проверено в браузерах:
Internet-explorer 11+;
Chrome 48+;
Opera 25+;
Mozilla firefox 40+;
Safari 9+.
Включена поддержка TypeScript.
Для чего создавалась эта библиотека и зачем она нужна?
Чтобы понять мотивы создания библиотеки, надо посмотреть на существующие популярные инструменты для JavaScript state-management, а именно: redux и mobx.
Redux
Это легковесная библиотека, которая весит всего 2kB и представляет единый контейнер управляемых состояний для js приложения. Основными плюсами redux являются его малый вес и гибкость. C помощью redux можно разрабатывать приложения любого размера. Мне лично нравится эта библиотека.
Но дьявол, как известно, в деталях.
Все не так просто, как кажется
Когда смотришь пример кода на GitHub возникает чувство, что все довольно просто. Но, когда дело касается применения в реальном проекте и если вы новичок, то у вас может просто-напросто, может вскипеть мозг. Вы не понимаете что, куда и как… Судорожно начинаете искать в интернете статьи и видео-уроки чтобы понять, как собрать все паттерны воедино. Redux-toolkit конечно сглаживает эту проблему, но лишь частично.
Отсутствие асинхронности из коробки.
Вероятно, в 2015, когда создавалась эта библиотека, это не было столь значимо. Сейчас на дворе 2021 и асинхронность повсюду во вселенной JavaScript. Конечно, эта проблема частично решается через middleware, такие как redux-saga и redux-thunk. Но это порождает еще две проблемы: отсутствие единого подхода и увеличение зависимостей проекта.
Отсутствие единого подхода
Redux лентяй и прокрастинатор… Он хочет что бы работу за него делали другие. Вам нужна асинхронность - подключайте отдельные библиотеки для слайд-эффектов, нужно избавится от лишних перерисовок - подключайте reselect, бесит писать reducers через switch – подключайте что-то типа redux-actions. Весь этот зверинец и порождает отсутствие единообразия.
Лишние зависимости от сторонних библиотек
Тут буду немногословен: лишние зависимости - не есть хорошо.
Вывод
Redux - хороший инструмент, но, я бы сказал, что он слишком много сваливает на разработчика.
Mobx
Основной лозунг mobx:
“Все, что может быть получено из состояния приложения, должно быть. Автоматически”.
Если redux - это лентяй и прокрастинатор, за которого нужно думать, то mobx - это скорее нарциссичный профессор в мире state-management. Он говорит: “Я все сделаю за тебя, а ты только включи творчество и нарисуй архитектуру».
Автоматика — это хорошо, особенно, если вы делайте одноразовый, быстрый проект, который после релиза уйдет в пыльный ящик или будет поддерживаться небольшой командой. Но если вы разрабатываете более крупный проект, над котором работают смежные команды, то от всей этой архитектурной свободы вы скорее всего получите максимум боли… Иной раз вы потратите не один час, чтобы понять, что откуда тянется.
Ни в коем случае не воспринимайте всё вышесказанное как хейт. Это всего лишь моё субъективное мнение, о плюсах и минусах данных инструментов.
Теперь можно поговорить о Biscuit
Цель biscuit-store - как раз заполнить нишу между прокрастинатором redux и профессором mobx. Создать некоего работягу, который ходит на завод и вытачивает заготовки. Он однообразно делает свою работу, не задавая лишних вопросов.
По задумке, biscuit должен быть максимально функционален, в меру автоматизирован и поощрять единый подход к архитектуре.
Перейдем к практике
Вы наверняка заметили утку на превью к статье и задались вопросом: «Причем тут утка?», речь же о бисквите… Тут нет каких-то безумных аналогий, просто я предпочитаю описывать создание контейнера состояний в biscuit по принципу трех шагов создания утки:
Создайте утку;
Донесите до утки, что она утка и должна, крякать, летать и плавать;
Научите утку крякать, летать и плавать.
Хватит слов, давайте поиграем в утиного бога
Итак, создадим нашу утку (хранилище состояний).
Если вы ранее использовали redux, то вы привыкли, что у вас есть одно хранилище состояний для всего приложения. Напротив, Biscuit-store поощряет создание нескольких контейнеров для разных абстрактных сегментов вашего приложения.
import { createStore } from '@biscuit-store/core';
export const { store, actions } = createStore({
name: 'duck',
initial: { value: '' },
});
Мы создали хранилище с минимально необходимыми настройками. То есть у нас теперь есть утка. Но она еще не осознает, что она утка и не умеет быть таковой.
Теперь мы должны донести до нее, что она утка и должна делать вещи, присущие уткам.
import { createStore } from '@biscuit-store/core';
import { adapter } from './adapter';
export const { store, actions } = createStore({
name: 'duck',
initial: { value: '' },
actions: {
duckSwim: 'duck/swim',
duckFly: 'duck/fly',
duckQuack: 'duck/quack',
},
middleware: [adapter]
});
В поле actions мы явно указали то, что должна уметь наша стейт-машина, то есть наша утка теперь знает, что ей надо делать некие действия. Теперь нам нужно научить утку делать те самые действия.
import { createAdapter } from '@biscuit-store/adapter';
const { action, connect } = createAdapter();
action('duck/swim', () => {
return { value: 'duck flews' };
});
action('duck/fly', () => {
return { value: 'duck flews' };
});
action('duck/quack', (payload, state, { send }) => {
// This is an asynchronous way of transmitting the payload
send({ value: 'duck quacks' });
});
export const adapter = connect;
Adapter - это модуль промежуточного программного обеспечения, который позволяет завязывать логику на состояния.
Наша утка готова отправится в большой мир.
Давайте проверим, на что она способна.
import { actions, store } from './store/duck'
const { duckQuack } = actions;
store.subscribe((state) => {
console.log(state.value); // 'duck quacks'
})
duckQuack.dispatch();
Еще можно вот так:
import { actions } from './store/duck'
const { duckQuack } = actions;
duckQuack.dispatch().after((current) => {
console.log(current); // 'duck quacks'
});
А так это будет выглядеть в React.
import { observer, useDispatch } from '@biscuit-store/react';
import { actions } from './store/duck';
const { duckQuack } = actions;
export default observer(
({ value }) => {
const [setQuack] = useDispatch(duckQuack);
return (
<div className='DuckWrapper'>
<p>action: {value}</p>
<button onClick={setQuack}>Duck quacks</button>
</div>
);
},
[duckQuack]
);
Вот небольшое демо.
На этом все, спасибо за внимание!
Biscuit-store молод и нуждается в поддержке
Biscuit еще очень молод и находится в стадии бета-тестирования.
Если вам понравилась эта библиотека, помогите ей развиваться звездочкой в GitHub'
KasperGreen
Интересно ваше мнение об effector
stranget1918 Автор
К сожалению, мне не приходилось иметь дело с данным инструментом. Но я обязательно с ним ознакомлюсь.