Recollect - это библиотека управления состоянием для React.

Он служит той же цели, что и Redux, с некоторыми ключевыми отличиями:

  • В IE не работает, потому что использует объект Прокси.
  • Поскольку он использует объект Proxy, вам больше не нужно беспокоиться о неизменности.

Уловка для вечеринки Recollect - это хранилище, которое ведет себя как обычный объект JavaScript, но внутренне неизменяемо. Итак, если вы хотите переключить полный статус задачи, вам больше не нужен такой код:

Вы можете просто написать todo.completed = !todo.completed.

Это похоже на изменение задачи, но это не так. Внутренне Recollect блокирует операцию и делает то же самое, что и ваш код Redux: создает новую версию хранилища с этим изменением и передает ее компонентам, которые должны быть повторно отрисованы.

Прежде чем я продолжу: я вообще не выбираю Redux. Это быстро, проверено на практике и является моей любимой библиотекой управления состоянием, когда мне нужно поддерживать Internet Explorer. Но если вы ориентируетесь на браузеры, которые поддерживают объект Proxy (~ 95% из них), то написание собственной логики неизменяемости похоже на написание собственной логики сборки мусора - в этом нет необходимости.

Вторая уловка Recollect заключается в том, что он знает, какие компоненты используют какие свойства в магазине, поэтому вам не нужна концепция «компонентов контейнера», чтобы указать, когда компоненты должны быть повторно отрисованы. Когда вы изменяете todo.completed, Recollect обновит компоненты, которые читают todo.completed при последней визуализации.

Если компонент нуждается в обновлении, он будет обновлен, не беспокойтесь об этом.

Хорошо, с учетом всего сказанного, нет особого смысла переходить с почтенного Redux на NKOTB Recollect, если вы не увидите значительного снижения сложности, так что ...

Дай мне статистику, СТАТИСТИКА

Приложение, которое легло в основу этой статистики, использовало redux, redux-thunk и react-redux. В нем было 50 компонентов, 9 из них - контейнерные, 25 создателей действий и 10 редукторов.

После преобразования в Recollect:

  • Все редукторы удалены
  • Все компоненты контейнера удалены
  • 6 из 25 создателей действий были удалены
  • (Почти) новый код не был добавлен

Это удалило 754 строки кода, которые существовали исключительно для описания того, как писать в магазин, не изменяя его - около 5% кодовой базы!

Размер пакета уменьшился на значительные 5 КБ (в сжатом виде). Отчасти потому, что Recollect немного меньше Redux (-1 КБ), но в основном из-за всего удаленного кода.

Для развлечения я также преобразовал пример todos в репозиторий Redux. Вот git diff, показывающий преобразование. К сожалению, GitHub по умолчанию скрывает удаленные файлы, так что это не дает того драматического визуального воздействия, которое мне хотелось бы.

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

(Если вы давний читатель, кое-что из этого может прозвучать в колокол - я писал о Recollect 18 месяцев назад, когда он был еще экспериментальным. С тех пор он сильно вырос и теперь готов к прайм-тайму.)

Как

Нулевой шаг - это изменение мышления: забудьте все, что вы знаете о неизменности. Относитесь к хранилищу Recollect как к простому старому объекту JavaScript. Вычеркните слово «неизменный» из своего словаря, вам больше не о чем беспокоиться (хотя вы встретите слово «неизменный» тысячу раз на этой странице, так что, возможно, не забывайте его значение только пока).

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

Если вам нужна эталонная реализация, вы можете установить и запустить demo directory в репо или загрузить его как CodeSandbox (знаете ли вы, что можете загружать URL-адреса GitHub в CodeSandbox? Я только недавно узнал об этом и думаю, что это супер большой.)

А если у вас возникнут вопросы, ознакомьтесь с FAQ в ридми.

Хорошо, давай продолжим! Для этого преобразования вы:

  1. Добавьте код в ваши создатели действий для записи в хранилище Recollect - реплицируя то, что написано в хранилище Redux.
  2. Обновите компоненты для чтения из хранилища Recollect, а не из хранилища Redux.
  3. Отключите создателей ваших действий от Redux.
  4. Удалите весь код, специфичный для Redux.
  5. Рефакторинг и упрощение там, где это необходимо.

Исходное состояние магазина Recollect

Если ваш магазин Redux имеет начальное состояние, вы захотите воспроизвести его в магазине Recollect.

Вы можете либо собрать объекты начального состояния из редукторов, либо записать содержимое хранилища Redux в консоль (с помощью store.getState()) сразу после вызова createStore() и его копирования.

Вам не нужно каким-либо образом «инициализировать» хранилище Recollect, но для удобства есть initStore функция, которая очистит хранилище и заменит его переданным вами объектом. Предполагая, что у вас есть исходное состояние как объект, который вы можете импортировать, ваши изменения могут выглядеть примерно так:

Если вы используете TypeScript, сейчас самое время определить форму объекта вашего магазина - расширив интерфейс Store Recollect. Создайте что-то вроде этого и положите куда-нибудь:

Размещение данных в магазине

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

Обновление объектов

Допустим, у вас есть простой синхронный создатель действий, подобный этому из документации Redux:

На этом этапе вы должны отследить все, что SET_VISIBILITY_FILTER обрабатывается в редукторе, и выяснить, как он меняет хранилище. Вот такой редуктор:

Это просто - нужно просто установить значение свойства. Итак, мы добавим новую строку в создатель действий, чтобы внести аналогичное изменение в хранилище Recollect:

Обратите внимание, что мы импортируем объект store прямо из Recollect. Вы можете считать, что ваши данные существуют «внутри» Recollect, и доступ к ним осуществляется путем импорта объекта store в модуль или когда объект store передается в качестве опоры вашим компонентам (о чем мы поговорим в следующем разделе).

У вас также могут быть редукторы, которые объединяют один объект с другим, например:

Это неизменная версия неглубокого слияния, которую мы можем воспроизвести с помощью Object.assign(store.config, config).

Я знаю, я знаю, что вы провели большую часть своей жизни, избегая этого первого слота в Object.assign, и будет странно втыкать туда что-то. Но я обещаю вам: невозможно изменить содержимое магазина Recollect, так что действуйте.

Добавление в массив

У вас может быть создатель действия, который отправляет действие, которое обрабатывается случаем редуктора, с чем-то вроде этого объективно ужасного кода:

Это неизменная версия todos.push(), поэтому мы бы добавили что-то вроде этого в существующий создатель действий:

Обновление элементов в массиве

Я уверен, что вы уже поняли суть - декодируйте неизменяемый код, чтобы получить изменяемую версию, и сделайте это с помощью хранилища Recollect.

Но давайте рассмотрим еще один пример, поскольку логика неизменяемости имеет тенденцию быть особенно грубой при обновлении элемента в массиве.

Само по себе это довольно плохо, но давайте пройдемся по всем остальным спагетти, которые у нас могут быть, чтобы отметить задачу как завершенную. Я пойму, если вы тускнеете, читая это:

  • Срабатывает событие щелчка в <Todo> компоненте.
  • <Todo> вызывает props.toggleTodo с props.index выбранной задачи - оба свойства переданы из <TodoList>, который был передан toggleTodo из <TodoListContainer>.
  • <TodoListContainer> завернут toggleTodo в dispatch, поэтому, когда он вызывается…
  • toggleTodo отправляет TOGGLE_TODO действие с index, переданным из <Todo> (который, кхм, передан из <TodoList>)
  • Случай редуктора улавливает это TOGGLE_TODO и обновляет задачу, мелко клонируя массив todos и todo в предоставленном index.

Способ Recollect:

  • Возникает событие щелчка в <Todo> компоненте.
  • <Todo> выполняет props.todo.completed = !props.todo.completed

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

Но все это всего лишь дразня - чего стоит ждать на этапе рефакторинга. А пока оставим спагетти на месте и обновим задачу в создателе действий, используя переданные в index:

Я был, возможно, немного напыщен, ранее заявлял, что «нового кода не было добавлено». Но, как вы могли заметить, до сих пор мы индивидуально заменяли строку кода, которая отправляет действие , описывающее, что должно быть сделано, строкой кода, которая это делает.

(Другой способ подумать об этом: вы все еще отправляете действие, просто синтаксис для отправки обновления в хранилище Recollect является теми же операторами JavaScript, которые вы используете для изменения объекта. Внутренне эти операции перехватываются и перевел в инструкцию по созданию следующей версии магазина.)

Более сложная логика

Если редуктор, обрабатывающий тип действия, имеет довольно много логики (и код замены довольно сложен), и этот тип действия отправляется из нескольких мест, вы можете заключить эту логику в функция обновления (о которой я расскажу через минуту). На данный момент я предлагаю продублировать код, который записывает в хранилище Recollect (ах!) - вставлять его рядом с каждой отправкой - и отбрасывать TODO, чтобы вернуться в конце, чтобы ОСУШИТЬ его (ungasp).

У вас также может быть код, который необходимо выполнить в результате других изменений в магазине. Например, если вы хотите устанавливать dirty: true при изменении определенных свойств, или синхронизировать некоторые данные с локальным хранилищем, или обновлять document.title при изменении страницы.

Для этого вы можете использовать функцию Recollect afterChange. Он будет выполнять обратный вызов каждый раз при изменении хранилища.

Я вообще не поклонник подобных побочных эффектов, потому что их трудно отследить, но они часто являются наименее ошибочным вариантом.

Убедившись, что все в порядке

По мере продвижения вы захотите убедиться, что магазин Recollect заполняется так, как вы ожидаете. Для этого может быть полезно использовать живые выражения Chrome в сочетании с __RR__.internals.store, чтобы показать живое изображение некоторой части хранилища Recollect в инструментах разработчика.

Вы также можете разбросать несколько console.assert операторов, чтобы сравнить два магазина.

Interlude: селекторы и апдейтеры

В Recollect нет правил о том, когда и где вы можете взаимодействовать с магазином. Вы можете import { store } from 'react-recollect' в любом файле. Или - как мы скоро увидим - используйте свойство store компонента collected. Это все тот же магазин.

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

  • Селекторы: код, который считывает данные из хранилища (аналогично селекторам и компонентам контейнера в Redux).
  • Программы обновления: код, обновляющий магазин (аналогично создателям и редукторам действий Redux).

Я задокументировал некоторые рекомендации в файле readme, поэтому я не буду здесь вдаваться в подробности, но я продолжу использовать термины селектор и средство обновления в этих конкретных значениях.

Конец интерлюдии. Продолжаем преобразование…

Компоненты контейнера

У вас полностью заполнен магазин Recollect, который скрывает ваш магазин Redux. Пришло время начать чтение из этого хранилища в ваших компонентах.

Это захватывающе, правда ?!

Я использую термин «компонент-контейнер», но для конкретности: я имею в виду код, который использует mapStateToProps и mapDispatchToProps с функцией react-redux connect для предоставления создателей данных и действий дочернему компоненту.

На этом этапе нам придется все сломать, пока мы делаем сразу несколько вещей:

  • Для любого компонента, который в настоящее время «завернут» в компонент-контейнер, оберните его collect, чтобы получить доступ к хранилищу Recollect в качестве свойства.
  • Обновите создателей действий, чтобы их можно было вызывать как обычные функции
  • Импортируйте эти функции в компоненты и вызывайте их напрямую
  • Удалить компоненты контейнера

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

Оборачивание компонента в сборку

Для начала оберните дочерний компонент компонента-контейнера в функцию Recollect collect и измените код компонента, чтобы он считывал данные из нового свойства store. Как и в случае с Redux, эта функция предоставляет данные и обновляет компонент при необходимости.

Если бы ваш компонент-контейнер выбирал из магазина только те свойства, которые требовались компоненту, код вашего компонента был бы красивым и сфокусированным:

С Recollect вы можете столкнуться с беспорядком, поскольку эта логика находится внутри компонента:

Тем более, если вы фильтруете / сортируете / запоминаете и т. Д.

Ваши варианты, вероятно, довольно очевидны:

  • встроить код, как указано выше
  • поместите его в функцию в верхней части файла - по сути, скопируйте / вставьте mapStateToProps.
  • поместите его в функцию в другом файле (и назовите "селектором")

Фактически, если вы действительно серьезно относились к разделению презентационных и контейнерных компонентов, ничто не помешает вам сохранить отдельный компонент только для передачи данных другому компоненту. (Хотя я бы сказал, что сейчас хорошее время, чтобы подвергнуть сомнению такие архитектурные« лучшие практики »).

Независимо от того, какой путь вы выберете, сейчас я предлагаю минимизировать рефакторинг. Просто скопируйте код выбора свойства из вашего компонента-контейнера в дочерний компонент, отбросьте TODO и вернитесь к нему в конце.

Если вы используете типы опор, я бы посоветовал добавить глобальный StorePropType объект, который вы можете импортировать и использовать в каждом компоненте, заключенном в collect. Поскольку проверка свойств считается «чтением» из магазина, Recollect подписал бы ваш компонент на все, что есть в магазине, если бы мы использовали prop-types. Итак, вы захотите заменить библиотеку prop-types эквивалентом PropTypes, экспортированным Recollect:

Никаких других изменений не требуется - внутри все та же библиотека. "Вы можете прочитать больше об этом здесь".

Если вы используете TypeScript, вы можете указать, что ваш компонент получит store как опору, используя тип WithStoreProp, описанный в Использование с TypeScript.

Если вы используете Flow, отлично, как это было раньше? Люди читают эти большие газеты в поезде? Средняя температура поверхности Земли немного прохладная? Берегись, астероид!

Вызов создателей действий напрямую

Вы заметите тонкое (приятное) изменение в «Новом способе ведения дел». С Redux вы не можете просто вызвать создателя действия как функцию. Redux должен «знать», когда вы вызываете эту функцию, чтобы он мог обрабатывать отправленную полезную нагрузку, отправлять ее редукторам для создания следующей версии хранилища и обновления с ее помощью соответствующих компонентов. В Recollect вся эта сантехника является внутренней, поэтому вы можете вызывать код обновления магазина, как любую другую функцию.

На этом этапе просмотрите каждого из создателей действий, которые вы передаете через компонент контейнера. В каждом из них удалите любой оператор return (который был бы отправлен редукторам) и удалите все явные вызовы dispatch.

Если вы используете redux-thunk, вы можете избавиться от функции return. Итак, создатель экшена выглядел так:

Станет так:

Если вы используете redux-saga, вы можете отказаться от function*, yield, call, put, take, fork, spawn и всего остального, что вам нужно для получения данных из API. Добро пожаловать на вечеринку _75 _ / _ 76_, вам здесь понравится.

Хорошо, доки, теперь, когда у вас есть функция, которую можно вызывать напрямую для обновления магазина, мы начнем называть ее «средством обновления», а не «создателем действий». Продолжайте и импортируйте каждое средство обновления в свой компонент и замените любой вызов props.doSomething() на doSomething().

Когда вы закончите этот шаг, у вас останется компонент-контейнер, который ничего не делает - все, что передано mapStateToProps, теперь будет доступно напрямую из props.store, все, что передано через mapDispatchToProps, будет импортировано как функция, а Recollect при необходимости позаботится о повторном рендеринге компонента.

Теперь вы можете удалить компонент-контейнер и (если это был отдельный файл) заменить ссылки на него ссылками на компонент, в который он заключен.

Я на 107% уверен, что сначала у вас все будет работать идеально, но если вы попадете в одну из -7%…

Отладка

Чтобы получить представление о том, что происходит внутри Recollect, введите __RR__.debugOn() в консоли браузера. Например, если я нажму на «задачу», чтобы отметить ее как завершенную с включенной отладкой, я увижу следующее:

Вы можете видеть, что Recollect установил значение в магазине, определил два компонента в очередь на обновление (и указывает, почему они обновляются), а затем запускает рендеринг (который, в свою очередь, показывает, что компонент читает из магазина, когда он рендеров).

Ведение журнала довольно подробное, а запись в консоль может быть медленной, поэтому, если вы наблюдаете некоторую задержку, введите __RR__.debugOff(), когда закончите.

Вы можете использовать помощник getComponentsByListener(), чтобы узнать, какие компоненты на какие свойства подписаны в магазине. Здесь мы видим три компонента, которые будут повторно отрисованы, если store.todoMvcPage.visibilityFilter изменится:

И наоборот, вы можете увидеть, на какие компоненты свойств подписаны с помощью помощника getListenersByComponent(). Вот свойства, на которые подписан <Footer>:

Наконец, я уже говорил, что вы можете получить доступ к магазину Recollect "откуда угодно", включая консоль инструментов разработчика как __RR__.internals.store. В некоторых сценариях устранения неполадок может помочь установить значение вручную и проверить, что пользовательский интерфейс обновляется соответствующим образом. Например, ввод первой строки ниже изменит имя первой задачи и вызовет повторную визуализацию, обновляя пользовательский интерфейс.

Подробнее об этих помощниках рассказано в ридми. Я очень хочу услышать, что еще вы найдете полезным при отладке; не стесняйся.

Есть несколько особых ошибок, о которых вам следует знать. Это не повлияет на 99% из вас (я надеюсь), но для полного раскрытия информации ...

Экзотические мутации

Я уже говорил, что невозможно изменить содержимое магазина, но на самом деле не все мутации блокируются.

Не волнуйтесь, обычные объекты JavaScript неизменяемы (объекты, массивы, карты и наборы) и, конечно же, примитивы неизменны (Undefined, Null, Boolean, Number, Symbol и String).

Но менее распространенные типы, такие как Uint8Array или DataView, могут быть видоизменены один раз в магазине. Кроме того, вы можете делать необычные вещи, например изменять ключ объекта записи карты (не имеет смысла «клонировать» ключ, поскольку в этом случае вы потеряете ссылку на связанную карту значение).

Эти исключения мутаций более подробно описаны в readme.

Изменение значений, которые Recollect не видит

Recollect будет повторно визуализировать компонент при изменении свойства, которое он использует. Быстрая викторина: если компонент не читает store.page.title при рендеринге, можем ли мы быть уверены, что его не нужно повторно рендерить, если store.page.title изменится?

Увы, нет. Например, следующий компонент должен быть повторно отрисован, если loading изменяется, но он никогда не читал свойство loading:

Это вызовет ошибку, поскольку Recollect не знает, как запустить рендеринг этого компонента при loading изменениях, то есть componentDidUpdate никогда не сработает. Решение состоит в том, чтобы сообщить Вспомните, что вы хотите, чтобы компонент был подписан на определенное свойство с помощью функции useProps. Просто вставьте его в свою функцию рендеринга ...

… И все готово.

У вас не возникнет этой проблемы, если вы используете хуки - она ​​возникает только в методах жизненного цикла компонентов класса, которые не запускаются во время рендеринга.

Я надеюсь, что передовые IE-дропперы, использующие Recollect, к настоящему времени также будут полностью оснащены хуками и никогда не столкнутся с этой проблемой.

(Я очень разочарован в себе из-за того, что не могу найти решение для этих проблем, и мои уши широко открыты для предложений.)

Очистить

Ты почти сделал!

  • Ваши данные записываются в магазин Recollect.
  • Ваши компоненты считываются из магазина Recollect.
  • Создатели ваших действий теперь являются разработчиками обновлений.
  • Вы удалили компоненты контейнера.

Теперь вы можете удалить исходную createStore логику Redux, удалить код, который предоставляет хранилище (с <Provider>), и удалить все ваши редукторы.

А затем, помолчав, npm uninstall redux react-redux redux-thunk.

Если вы использовали библиотеку prop-types, вам следует заменить ее везде на Recollect PropTypes, а затем удалить prop-types.

Окончательный рефакторинг

Теперь у вас есть приложение, использующее Recollect, но с архитектурой, которая, возможно, была разработана для Redux. Посмотрим, есть ли какие-то вещи, которые мы можем привести в порядок, прежде чем прекращать работу.

Компоненты развязки

В примере приложения, о котором я упоминал ранее, хотя у меня было только 9 (Redux) компонентов-контейнеров, я закончил с 16 (Recollect) собранными компонентами.

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

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

Первый шаг (на этом шаге разделения) - определить, куда вы передаете данные от родителя к потомку, к которым ребенок может получить доступ непосредственно из магазина. То же самое можно сделать и с функциями - нет необходимости передавать функцию обновления от родителя к потомку, когда потомок может просто импортировать ее напрямую.

Извлечение селекторов

Теперь может быть время создать несколько специальных файлов «селекторов», если эта роль ранее выполнялась компонентами контейнера. Или сохраните их как функции в своих компонентах. Все, что плывет на вашей лодке.

Встраивание обновлений

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

В частности, если у вас есть создатель действий, который существует только для установки значения одного свойства в магазине. Это отличный кандидат для встраивания.

И снова вы заменяете строку кода, в которой говорится: «Я собираюсь выполнить X», на строку кода, которая делает это.

Упреждающее опровержение: «разделение проблем» не является оправданием для введения ненужной сложности, так что даже не начинайте… На самом деле радость React в том, что вы можете думать о своем пользовательском интерфейсе как о визуальном представлении ваших данных. Ваши данные должны существовать везде, где существует ваш пользовательский интерфейс, потому что ваш пользовательский интерфейс - это просто ваши данные в облачении. И говорить, что можно читать из магазина в строке 42, но не писать в магазин в строке 46, бессмысленно. Не упорядочивайте свой код на основе расплывчатых идеологий, упорядочивайте его так, чтобы максимизировать удобочитаемость, удобство сопровождения, компоновку и т. Д. [Дыхание] В общем: любое решение, которое увеличивает что-то, заканчивающееся на «способность», - хорошо. Все, что вы делаете, потому что слышите об этом «лучшая практика», но не можете объяснить почему, - плохо. Спасибо за внимание, воображаемый спорщик, я рад, что мне удалось так легко изменить ваше мировоззрение.

Лично я оценил всю свою логику обновления магазина и встроил ее в компоненты, соответствующие этим критериям:

а) он не был передан

б) это было синхронно

в) это было менее 11 строк кода или менее 13 строк кода, и в тот день я видел три красные машины.

И как оказалось, этим условиям удовлетворяли 6 из 25 моих создателей экшенов.

Как упоминалось ранее, обновление элемента в массиве становится особенно глупым при написании собственного кода неизменяемости вручную. Я предположил, что вы можете просто редактировать объект элемента массива прямо в компоненте, например props.todo.completed = !props.todo.completed, и сейчас самое время внести эти изменения. Любой создатель действия, который принимает «индекс» для обновления элемента в массиве, является кандидатом на удаление. В качестве альтернативы вы можете передать фактический объект, который хотите обновить, например updateTodoALot(props.todo). Я бы посоветовал сделать это, если логика обновления содержит более 15 строк кода и вы не видели ни одной красной машины в течение как минимум недели.

Тесты

В преобразованном мной проекте было несколько сотен тестов - около двух третей интеграции, одна треть блока. Ни один из интеграционных тестов не требовал внимания (очевидно?), Но, конечно, все модульные тесты компонентов и создателей действий нуждались во внимании, а все тесты редукторов требовали хорошего жесткого удаления.

Если у вас есть неработающие модульные тесты (и вы хотите их сохранить, что не должно быть само собой разумеющимся):

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

Тесты создателя действий станут тестами "средства обновления" и должны следовать довольно простой схеме:

  1. Поместите данные в магазин (Упорядочить / Дать)
  2. Вызов разработчика обновлений (Действие / Когда)
  3. Убедитесь, что магазин выглядит правильно (Утвердить / Затем)

Если вы хотите проверить, как обновлялся магазин (например, был установлен статус загрузки, затем данные получены, затем статус загрузки обновлен) , вы можете использовать Recollect's afterChange function для записи каждого отдельного изменения в магазин. Многие из собственных тестов Recollect передают afterChange имитационную функцию Jest для записи активности магазина, вот один пример.

Как для апдейтера, так и для компонентных тестов вы, скорее всего, захотите сбросить хранилище между тестами, что вы можете сделать, вызвав initStore(). Вы даже можете сделать это в глобальном beforeEach, если ваша тестовая среда поддерживает такую ​​вещь.

Конец

Привет, спасибо, что прочитали. Как прошло? Тебе было весело? Какие-нибудь проблемы? Есть какие-нибудь умные идеи, которые упростили преобразование? Дай мне знать в комментариях.