Русский
Русский
English
Статистика
Реклама

Lens JS как менеджер состояния приложения

Обзор библиотеки lens-js и эксперименты с котиками.

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

Линзы

И так, что же такое Линзы? Проще всего ответить тезисно - линзы это:

  • принцип организации работы с данными, где те квантуются по отдельным узлам в одном большом направленном графе;

  • агрегатор (редьюсер), который занимается сборкой всех отдельных квантов по всем правилам функциональной парадигмы;

  • интерфейс, который обеспечивает доступ к данным каждого кванта;

  • и последнее, линза обеспечивает целостность данных и их актуальность в Вашем приложении.

Тут вот стоит отметить, что мы ещё не говорим о как-либо реализации. Линза это хорошая сказка для тех, кто уже устал от Redux, MobX и т. п. или разрабатывает какой-нибудь специфический модуль, где проблематично использовать популярные менеджеры состояний. Реализаций линз множество. Попробуйте их все!

Как это всё работает?

Чем объяснять на пальцах, гораздо проще сделать это на каком-нибудь примере. Будем экспериментировать на котиках. Не волнуйтесь! Ни один котик не пострадает! Приступим

Рассмотрим демонстрационное приложение react-lens-cats, разработанное на основе линз. В нём используется библиотека lens-js, а также её обёртки для TypeScript и React lens-ts и react-lens.

Займёмся постановкой задачи: Представим, что несколько котиков решили прокатиться на карусели. Нам нужно организовать их очередь и распределить занятые места между ними. Формально это два массива, между которыми котики будут перемещаться.

Пусть структура данных выглядит следующим образом:

export interface Cat {    name: string;}export interface Queue {    cats: Cat[]}export interface Store {    street: Queue;    circle: Queue;}

Давайте создадим наше состояние и объявим линзу в фале lens.ts

import { Lens } from '@vovikilelik/react-ts';const murzic: Cat = { name: 'Murzic' };const pushok: Cat = { name: 'Pushok' };const sedric: Cat = { name: 'Sedric' };const rizhik: Cat = { name: 'Rizhik' };const store: {lens: Store} = {    lens: {        street: { cats: [murzic, pushok, sedric, rizhik] },        circle: { cats: [] }    }};export const lens = new Lens<Store>(    () => store.lens,    (value, effect) => {        store.lens = value;        effect();    });

Пока всё легко и понятно? Но не будем отвлекаться, а то получится как на "матане".

Как можно заметить, мы экспортируем только константу lens, относительно которой и будем работать с состоянием. Это синглтон, "детка".

Будет не так интересно, если мы просто пойдём по приведённому исходному тексту. Давайте попробуем разобраться с самых азов? В качестве эксперимента, создадим Test.tsx и попробуем получить доступ к свойствам нашего состояния.

import { lens } from './lens';export Test: React.FC = () => (    <div>      { lens.go('circle').go('cats').get().map(c => c.name).join(' ') }      { lens.go('street').go('cats').get().map(c => c.name).join(' ') }    </div>);

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

Автоподстановка вложенных свойствАвтоподстановка вложенных свойств

Но постойте, судари! У нас тут что-то не так с кодом! И действительно, массивы circle и street одинаковы. Выгоднее получить универсальный компонент, который отображает только массив котиков, откуда бы мы его не взяли. А давайте, просто, передавать узел линзы, как параметр компонента, вот так:

import { Lens } from '@vovikilelik/lens-ts';import { useLens } from '@vovikilelik/react-lens';import { Cat } from './lens';export Cats: React.FC = (cats: Lens<Cat[]>) => {    const [catsArray] = useLens(cats);    return (      <div>          { catsArray.map(c => c.name).join(' ') }      </div>    );}

Тогда файл Test.tsx будет выглядеть следующим образом:

import { lens } from './lens';export Test: React.FC = () => (    <div>        <Cats cats={lens.go('circle').go('cats')} />        <Cats cats={lens.go('street').go('cats')} />    </div>);

Всё ещё сложно. Для чего мы постоянно обращаемся к корневой линзе? Нам же не так и важно, где хранятся массивы котиков? Давайте сделаем Test.tsx более универсальным, ну и назовём его как-нибудь подходяще - Lunapark.tsx:

import { Lens } from '@vovikilelik/lens-ts';import { Queue } from './lens';export Lunapark: React.FC = (street: Lens<Queue>, circle: Lens<Queue>) => (    <div>        <Cats cats={street.go('cats')} />        <Cats cats={circle.go('cats')} />    </div>);

Другое дело. Как ловко мы тут всё инкапсулировали...

Теперь вид компонента упростился. Но смотрите, проявилась одна особенность, таким образом, мы можем создавать целые модели компонентов, которые будут также универсальны, как и рядовые.

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

Теперь дело за малым, переместить котиков из одного массива в другой. Давайте в нашем Lunapark.tsx создадим кнопку с обработчиком, где мы и завершим нашу задумку.

const popCat = (lens: Lens<Cat[]>): Cat | undefined => {    const cats = lens.get();    const cat = cats.pop();    lens.set(cats);    return cat;}const playCat = (lens: Lens<Cat[]>, cat: Cat) => {    lens.set([...lens.get(), cat]);}export Lunapark: React.FC = (street: Lens<Queue>, circle: Lens<Queue>) => {  const onCatPlay = useCallback(() => {     const cat = popCat(street.go('cats'));     cat && playCat(circle.go('cats'), cat);  }, [street, circle]);    return (    <div>        <Cats cats={street.go('cats')} />        <Cats cats={circle.go('cats')} />        <button onClick={onCatPlay} />    </div>  );}

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

Что же, получилось весьма недурно.

А есть ещё какая-нибудь польза?

Линзы это не строго привязанная, к какой-либо технологии, библиотека. Их успешно можно применять там, где не подходят привычные подходы. Например, при создании динамических сцен на BabylonJS или внутреннего состояния Web-компонентов и т. п. Линзы это не аналог Redux или похожих технологий и, как следствие, могут применяться совместно, как абстракция более низкого уровня. Всё дело в воображении

Выводы

Что, уже? Да. Целью статьи было познакомить читателя с ещё одним подходом для организации состояния приложения. Мы рассмотрели самое-самое главное потенциал к масштабируемости и области применения.

Давайте подытожим! С помощью линз можно:

  • реализовывать состояние приложения;

  • разрабатывать общие компоненты и модели;

  • организовывать событийно-ориентированные подпрограммы;

  • создавать абстракции для работы с другими подходами к управлению состоянием.

Ссылки

  1. Wiki по lens-js

  2. Проект с котиками на lens-js

  3. Пакет react-lens на npm

  4. Пакет lens-ts на npm

  5. Пакет lens-js на npm

  6. Статья про другие линзы

  7. Ещё интересный пост про линзы

Источник: habr.com
К списку статей
Опубликовано: 23.01.2021 14:23:52
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Javascript

Функциональное программирование

Линзы

Lens

React

Typescript

State management

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru