Интенсивное чтение на этой неделе«Переосмысление Redux».
1. Введение
Переосмысление ReduxrematchавторShawn McKayСухая статья написана.
dvaПосле этого существует множество фреймворков управления состоянием, основанных на избыточности, но большинство из них ограничены или даже отстали. Но пока я не увидел матч-реванш, я, наконец, почувствовал, что сообщество Redux пошло еще дальше.
Ценность этой статьи заключается в том, что, помимо концепций Mobx и RXjs и только глубокого переосмысления редукции, она очень полезна для большинства инженерных сценариев, которые все еще используют редукс.
2 Обзор
Что относительно ново, так это то, что автор дает формулу для оценки качества фреймворка или инструмента:
工具质量 = 工具节省的时间/使用工具消耗的时间
Если мы оценим нативный избыточность таким образом, мы обнаружим, что дополнительное время, необходимое для использования избыточности, может превышать сэкономленное время, и с этой точки зрения избыточность снизит эффективность работы.
Но идея управления данными в redux верна, и сложные front-end проекты действительно нуждаются в этой идее.Чтобы использовать redux более эффективно, нам нужно использовать фреймворк на основе redux. Автор объясняет, какие проблемы должен решить фреймворк, основанный на редуксе, с шести точек зрения.
Упрощенная инициализация
Код инициализации Redux включает в себя множество концепций, таких какcompose
thunk
и так далее, покаreducer
,initialState
,middlewares
Эти три важные концепции разделены на вызовы в стиле функций, а не на более приемлемый стиль конфигурации:
const store = preloadedState => {
return createStore(
rootReducer,
preloadedState,
compose(applyMiddleware(thunk, api), DevTools.instrument())
);
};
Если вы измените метод конфигурации, стоимость понимания будет значительно снижена:
const store = new Redux.Store({
instialState: {},
reducers: { count },
middlewares: [api, devTools]
});
Примечание автора: метод инициализации redux очень функционален, а следующий метод настройки более объектно-ориентирован. Напротив, объектно-ориентированный способ лучше понять, ведь хранилище — это объект.
instialState
Существует также та же проблема, по сравнению с оператором отображения, будетpreloadedState
Как параметр функции, он более абстрактен, и присваивание редукса начальному состоянию также относительно скрыто.createStore
Неудобно назначать равномерно одновременно, потому что редукторы разбросаны.Если вы назначаете значения в редукторах, вам нужно использовать функцию параметра по умолчанию es, которая больше похожа на бизнес-мышление, чем на возможность, предоставляемую редуксом.
Упростить редукторы
Гранулярность редуктора Redux слишком велика, что не только приводит к ручному сопоставлению внутри функций.type
, тоже принесtype
,payload
д., чтобы понять стоимость:
const countReducer = (state, action) => {
switch (action.type) {
case INCREMENT:
return state + action.payload;
case DECREMENT:
return state - action.payload;
default:
return state;
}
};
Было бы понятнее установить редьюсеры в конфиге, например, определить объект:
const countReducer = {
INCREMENT: (state, action) => state + action.payload,
DECREMENT: (state, action) => state - action.payload
};
Поддержка асинхронного/ожидания
Поддержка динамических данных в редуксе пока довольно трудоемка, нужно разбираться в функциях высшего порядка и понимать, как используется middleware, иначе не будешь знать, почему правильно писать:
const incrementAsync = count => async dispatch => {
await delay();
dispatch(increment(count));
};
Почему бы не стереть стоимость понимания и просто позволитьasync
А типы действий?
const incrementAsync = async count => {
await delay();
dispatch(increment(count));
};
Примечание автора: Мы обнаружили, что способ рематча, диспетчеризация импортируется (глобальные переменные), а диспетчер редукса вводится.На первый взгляд кажется, что редукс более разумен, но на самом деле я предпочитаю решение рематча. После многолетней практики лучше не использовать потоки данных для компонентов.Достаточно только одного экземпляра потока данных проекта.Дизайн глобальной диспетчеризации на самом деле более разумен, в то время как дизайн инъекционной диспетчеризации, кажется, следует идеально подходит для технологий, но игнорирует сценарии использования в бизнесе, что приводит к излишним и ненужным проблемам.
Изменить действие + редуктор на два действия
Обвинение между абстрактным действием редукса и редуктором очень четкое: действие отвечает за изменение всего, кроме хранилища, а редуктор отвечает за изменение хранилища и иногда используется для обработки данных. Эта концепция на самом деле довольно расплывчата, потому что часто неясно, где обработка данных находится в действии или редюсере, а слишком простой редюсер должен написать действие, соответствующее ему, что кажется слишком формальным и громоздким.
Если переосмыслить проблему, у нас есть только два типа действий:reducer action
а такжеeffect action
.
- действие редуктора: сменить магазин.
- Действие эффекта: обрабатывает асинхронные сценарии, может вызывать другие действия и не может изменять хранилище.
Синхронные сценарии могут обрабатываться функцией редуктора, требуются только асинхронные сценарии.effect action
С асинхронной частью разобрались, а синхронная часть все еще передана функции редуктора, и обязанности этих двух действий более понятны.
Больше не показывать тип действия объявления
Прекратите хранить типы действий в файле,const ACTION_ONE = 'ACTION_ONE'
На самом деле, я неоднократно писал строку, напрямую используя ключ объекта для представления значения действия и добавляя имя хранилища в качестве префикса для обеспечения уникальности.
В то же время Redux рекомендует использоватьpayload
ключ для передачи значения, так почему бы не заставить его использоватьpayload
как ввод, но черезaction.payload
Как насчет стоимости? Использовать напрямуюpayload
Это не только визуально уменьшает объем кода, его легко понять, но также налагает ограничения на стиль кода, позволяя реализовывать предложения.
Редуктор прямо как ActionCreator
Redux вызывает действие более громоздко, используйтеdispatch
или пропустить редуктор черезActionCreator
обертка функций. Почему бы просто не обернуть редуктор автоматическиActionCreator
Шерстяная ткань? Сократите шаблонный код и сделайте каждую строку кода значимой.
Наконец, автор даетrematch
Полный пример:
import { init, dispatch } from "@rematch/core";
import delay from "./makeMeWait";
const count = {
state: 0,
reducers: {
increment: (state, payload) => state + payload,
decrement: (state, payload) => state - payload
},
effects: {
async incrementAsync(payload) {
await delay();
this.increment(payload);
}
}
};
const store = init({
models: { count }
});
dispatch.count.incrementAsync(1);
3 Интенсивное чтение
Я думаю, что эта статья в основном тщательно анализирует инженерные проблемы редукции, а также дает очень хорошую реализацию.
Максимальная оптимизация деталей
Первый заключается в непосредственном использованииpayload
вместо целогоaction
В качестве входного параметра усиливаются ограничения и упрощается сложность кода:
increment: (state, payload) => state + payload;
Второе использованиеasync
В функции эффектов используйтеthis.increment
Вызов функции, заменаput({type: "increment"})
(dva), который имеет поддержку типов в машинописном тексте, может не только использовать автоматические переходы для замены поиска строк, но и проверять типы параметров, что очень редко встречается в среде редукции.
Наконец вdispatch
Функция также предоставляет два метода вызова:
dispatch({ type: "count/increment", payload: 1 });
dispatch.count.increment(1);
Если для лучшей поддержки типа или маскиpayload
концепции, вы можете использовать второе решение, чтобы снова упростить концепцию редукции.
Встроено больше плагинов
rematch интегрирует часто используемые повторный выбор, сохранение, погружение и т. д. в плагины, что относительно усиливает концепцию экологии плагинов. В потоке данных есть дополнительные возможности для кэширования данных, оптимизации производительности и оптимизации процесса разработки.Использование экологии подключаемых модулей является хорошим направлением развития.
Напримерrematch-immerПлагины, вы можете изменить магазин изменяемым образом:
const count = {
state: 0,
reducers: {
add(state) {
state += 1;
return state;
}
}
};
Но immer не будет работать, когда состояние не является объектом, поэтому лучше разработатьreturn state
привычка.
Наконец, поговорим о недостатках.reducers
Объявление несовместимо с параметром вызова.
Объявление редукторов несовместимо с параметрами вызова
Например, следующие редукторы:
const count = {
state: 0,
reducers: {
increment: (state, payload) => state + payload,
decrement: (state, payload) => state - payload
},
effects: {
async incrementAsync(payload) {
await delay();
this.increment(payload);
}
}
};
когда определеноincrement
два параметра, иincrementAsync
При его вызове есть только один параметр, что может ввести в заблуждение.state
помещатьthis
середина:
const count = {
state: 0,
reducers: {
increment: payload => this.state + payload,
decrement: payload => this.state - payload
},
effects: {
async incrementAsync(payload) {
await delay();
this.increment(payload);
}
}
};
Конечно, метод повторного сопоставления сохраняет характер функции без побочных эффектов, и можно видеть, что были сделаны некоторые компромиссы.
4 Резюме
Повторю авторскую формулу качества инструмента:
工具质量 = 工具节省的时间/使用工具消耗的时间
Если инструмент может сэкономить время разработки, но требует больших затрат при использовании, не спешите использовать его в проекте, пока не выясните, как снизить стоимость использования, это самое большое вдохновение, которое я получаю.
Наконец, я хотел бы поблагодарить автора rematch за дух совершенства, который привносит дальнейшую экстремальную оптимизацию в редукс.
еще 5 обсуждений
Адрес обсуждения:Интенсивное чтение «Переосмысление Redux» · Выпуск №83 · dt-fe/weekly
Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам.