Эта статья будет написана с нуляRedux
, если ты правRedux
Использование и исходный код .Redux
Бар. Знай это, знай почему.
Код, соответствующий этой статье, находится в:Github.com/yvette la U/b ..., рекомендуется сначалаclone
код, прочитайте эту статью против кода.
1. Redux
что это такое?
Redux
даJavaScript
Контейнер состояния, обеспечивающий предсказуемое управление состоянием.Redux
кроме иReact
Помимо совместного использования, также поддерживаются другие интерфейсные библиотеки.Redux
Маленький и мощный, только2KB
. Здесь нам нужно внести ясность:Redux
а такжеReact
Между ними нет прочной связующей связи. Эта статья направлена на то, чтобы понять и реализоватьRedux
, но не включаетreact-redux
(Вы можете глубоко понять одну точку знания за раз,react-redux
появится в следующей статье).
2. С нуля для достиженияRedux
Давайте забудемRedux
понятие, начиная с примера, используяcreate-react-app
Создайте проект:to-redux
.
Кодовый адрес:myredux/to-redux
середина.
Будуpublic/index.html
серединаbody
Изменено следующим образом:
<div id="app">
<div id="header">
前端宇宙
</div>
<div id="main">
<div id="content">大家好,我是前端宇宙作者刘小夕</div>
<button class="change-theme" id="to-blue">Blue</button>
<button class="change-theme" id="to-pink">Pink</button>
</div>
</div>
Функция, которую мы хотим реализовать, показана на рисунке выше.При нажатии кнопки можно изменить цвет шрифта всего приложения.
Исправлятьsrc/index.js
следующим образом (код:to-redux/src/index1.js
):
let state = {
color: 'blue'
}
//渲染应用
function renderApp() {
renderHeader();
renderContent();
}
//渲染 title 部分
function renderHeader() {
const header = document.getElementById('header');
header.style.color = state.color;
}
//渲染内容部分
function renderContent() {
const content = document.getElementById('content');
content.style.color = state.color;
}
renderApp();
//点击按钮,更改字体颜色
document.getElementById('to-blue').onclick = function () {
state.color = 'rgb(0, 51, 254)';
renderApp();
}
document.getElementById('to-pink').onclick = function () {
state.color = 'rgb(247, 109, 132)';
renderApp();
}
Это приложение очень простое, но у него есть одна проблема:state
является общим состоянием, но любой может изменить его, как только мы изменим это состояние по своему желанию, это может вызвать ошибки, например, вrenderHeader
внутри, наборstate = {}
, легко вызвать непредвиденные ошибки.
Однако во многих случаях нам нужно делиться состоянием, поэтому мы можем рассмотреть возможность установки некоторых порогов.Например, мы соглашаемся с тем, что глобальное состояние не может быть изменено напрямую и должно быть изменено определенным образом. Для этого мы определяемchangeState
функция, и она отвечает за изменение глобального состояния.
//在 index.js 中继续追加代码
function changeState(action) {
switch(action.type) {
case 'CHANGE_COLOR':
return {
...state,
color: action.color
}
default:
return state;
}
}
Мы договорились, что толькоchangeState
чтобы изменить состояние, он принимает параметрaction
,Включатьtype
Поле обычных объектов,type
Поля используются для определения типа вашего действия (т.е. как изменить состояние).
Мы хотим нажать кнопку, чтобы изменить цвет шрифта всего приложения.
//在 index.js 中继续追加代码
document.getElementById('to-blue').onclick = function() {
let state = changeState({
type: 'CHANGE_COLOR',
color: 'rgb(0, 51, 254)'
});
//状态修改完之后,需要重新渲染页面
renderApp(state);
}
document.getElementById('to-pink').onclick = function() {
let state = changeState({
type: 'CHANGE_COLOR',
color: 'rgb(247, 109, 132)'
});
renderApp(state);
}
оторваться от магазина
Хотя теперь мы договариваемся о том, как модифицировать состояние, ноstate
является глобальной переменной, мы можем легко изменить ее, поэтому мы можем рассмотреть возможность сделать ее локальной переменной, определив ее внутри функции (createStore
), но внешне тоже нужно использоватьstate
поэтому нам нужно предоставить методgetState()
, так что мыcreateStore
полученный извнеstate
.
function createStore (state) {
const getState = () => state;
return {
getState
}
}
Теперь мы можем пройтиstore.getState()
способ получения статуса (здесь необходимо пояснить, чтоstate
Обычно объект, поэтому этот объект может быть непосредственно модифицирован снаружи, но если глубокая копияstate
Возвращение, то он не должен быть изменен извне, так какredux
Исходный код возвращается напрямуюstate
, здесь мы не выполняем глубокое копирование (в конце концов, это стоит производительности).
Недостаточно просто получить состояние, нам также нужен метод для изменения состояния, теперь состояние является приватной переменной, мы должны также поставить метод для изменения состоянияcreateStore
, и предоставить его для внешнего использования.
function createStore (state) {
const getState = () => state;
const changeState = () => {
//...changeState 中的 code
}
return {
getState,
changeState
}
}
Сейчас,index.js
Средний код становится следующим (to-redux/src/index2.js
):
function createStore() {
let state = {
color: 'blue'
}
const getState = () => state;
function changeState(action) {
switch (action.type) {
case 'CHANGE_COLOR':
state = {
...state,
color: action.color
}
return state;
default:
return state;
}
}
return {
getState,
changeState
}
}
function renderApp(state) {
renderHeader(state);
renderContent(state);
}
function renderHeader(state) {
const header = document.getElementById('header');
header.style.color = state.color;
}
function renderContent(state) {
const content = document.getElementById('content');
content.style.color = state.color;
}
document.getElementById('to-blue').onclick = function () {
store.changeState({
type: 'CHANGE_COLOR',
color: 'rgb(0, 51, 254)'
});
renderApp(store.getState());
}
document.getElementById('to-pink').onclick = function () {
store.changeState({
type: 'CHANGE_COLOR',
color: 'rgb(247, 109, 132)'
});
renderApp(store.getState());
}
const store = createStore();
renderApp(store.getState());
Хотя мы сейчас оторвалисьcreateStore
метод, но очевидно, что этот метод вовсе не универсален,state
а такжеchangeState
методы определены вcreateStore
середина. В этом случае другие приложения не могут повторно использовать этот режим.
changeState
Логика должна быть определена снаружи, поскольку логика для модификации состояния обязательна для каждого приложения. Мы разбиваем эту часть логики снаружи и переименуйте ееreducer
(Спросите, почему это называетсяreducer
, вопрос за иredux
быть последовательным).reducer
Для чего она, грубо говоря, основана наaction
типа вычисляется новое состояние. потому что это неcreateStore
Определено внутри, недоступно напрямуюstate
, поэтому нам нужно представить его как параметр, передаваемый в состояние. следующим образом:
function reducer(state, action) {
switch(action.type) {
case 'CHANGE_COLOR':
return {
...state,
color: action.color
}
default:
return state;
}
}
createStore Evolved
function createStore(reducer) {
let state = {
color: 'blue'
}
const getState = () => state;
//将此处的 changeState 更名为 `dispatch`
const dispatch = (action) => {
//reducer 接收老状态和action,返回一个新状态
state = reducer(state, action);
}
return {
getState,
dispatch
}
}
для разных приложенийstate
должно быть иначе, мы будемstate
Значение определяется вcreateStore
Внутренне он должен быть иррациональным.
function createStore(reducer) {
let state;
const getState = () => state;
const dispatch = (action) => {
//reducer(state, action) 返回一个新状态
state = reducer(state, action);
}
return {
getState,
dispatch
}
}
Вниманиеreducer
Кstate
Есть начальное состояние, которое на самом деле очень простое.state
Значение инициализации равноreducer
значение параметра по умолчанию, затем вcreateStore
распространять одинreducer
看不懂的动作就可以了。 такgetState
При первом вызове можно получить значение состояния по умолчанию.
createStore Evolved 2.0
function createStore(reducer) {
let state;
const getState = () => state;
//每当 `dispatch` 一个动作的时候,我们需要调用 `reducer` 以返回一个新状态
const dispatch = (action) => {
//reducer(state, action) 返回一个新状态
state = reducer(state, action);
}
//你要是有个 action 的 type 的值是 `@@redux/__INIT__${Math.random()}`,我敬你是个狠人
dispatch({ type: `@@redux/__INIT__${Math.random()}` });
return {
getState,
dispatch
}
}
теперь этоcreateStore
Его можно использовать везде, но чувствуете ли вы, что каждый разdispatch
После этого оба вручнуюrenderApp()
Кажется глупым, текущее приложение вызывается два раза, если нужно модифицировать 1000 разstate
Это вружно звонить в 1000 раз?renderApp()
?
Можешь упростить? Вызывается автоматически каждый раз при изменении данныхrenderApp()
. Конечно, мы не можемrenderApp()
написать наcreateStore()
изdispatch
, потому что в других приложениях имя функции может не вызыватьсяrenderApp()
, и можно вызвать болееrenderApp()
. можно импортировать здесь发布订阅模式
, уведомлять всех подписчиков об изменении состояния.
createStore Evolved 3.0
function createStore(reducer) {
let state;
let listeners = [];
const getState = () => state;
//subscribe 每次调用,都会返回一个取消订阅的方法
const subscribe = (ln) => {
listeners.push(ln);
//订阅之后,也要允许取消订阅。
//难道我订了某本杂志之后,就不允许我退订吗?可怕~
const unsubscribe = () => {
listeners = listeners.filter(listener => ln !== listener);
}
return unsubscribe;
};
const dispatch = (action) => {
//reducer(state, action) 返回一个新状态
state = reducer(state, action);
listeners.forEach(ln => ln());
}
//你要是有个 action 的 type 的值正好和 `@@redux/__INIT__${Math.random()}` 相等,我敬你是个狠人
dispatch({ type: `@@redux/__INIT__${Math.random()}` });
return {
getState,
dispatch,
subscribe
}
}
Пока самый простойredux
был создан,createStore
даredux
Основной. Давайте воспользуемся этой сокращенной версиейredux
переписать наш код,index.js
Содержимое файла обновляется следующим образом (to-redux/src/index.js
):
function createStore() {
//code(自行将上面createStore的代码拷贝至此处)
}
const initialState = {
color: 'blue'
}
function reducer(state = initialState, action) {
switch (action.type) {
case 'CHANGE_COLOR':
return {
...state,
color: action.color
}
default:
return state;
}
}
const store = createStore(reducer);
function renderApp(state) {
renderHeader(state);
renderContent(state);
}
function renderHeader(state) {
const header = document.getElementById('header');
header.style.color = state.color;
}
function renderContent(state) {
const content = document.getElementById('content');
content.style.color = state.color;
}
document.getElementById('to-blue').onclick = function () {
store.dispatch({
type: 'CHANGE_COLOR',
color: 'rgb(0, 51, 254)'
});
}
document.getElementById('to-pink').onclick = function () {
store.dispatch({
type: 'CHANGE_COLOR',
color: 'rgb(247, 109, 132)'
});
}
renderApp(store.getState());
//每次state发生改变时,都重新渲染
store.subscribe(() => renderApp(store.getState()));
Если теперь мы хотим нажатьPink
После этого цвет шрифта менять нельзя, тогда тоже можно отписаться:
const unsub = store.subscribe(() => renderApp(store.getState()));
document.getElementById('to-pink').onclick = function () {
//code...
unsub(); //取消订阅
}
Кстати:reducer
Это чистая функция (если вы не понимаете концепцию чистой функции, проверьте информацию сами), она получает предыдущийstate
а такжеaction
, И возвращает новыйstate
. не спрашивай почемуaction
должен иметь вtype
поле, это просто условность (redux
Так было задумано)
Наследие: почемуreducer
Обязательно верните новыйstate
, вместо прямого измененияstate
Шерстяная ткань. Не стесняйтесь оставлять свой ответ в области комментариев.
Мы выводили шаг за шагомredux
Основной код , теперь давайте рассмотримredux
Дизайнерское мышление:
Redux
дизайн-мышление
-
Redux
все состояние приложения (state
) к месту (обычно мы называем егоstore
) - Когда нам нужно изменить состояние, мы должны отправить (
dispatch
)Одинaction
(action
это сtype
полевой объект) - Специализированные обработчики состояний
reducer
получить старыйstate
а такжеaction
, и вернет новыйstate
- пройти через
subscribe
Настройте подписку, чтобы уведомлять всех подписчиков каждый раз, когда отправляется действие.
Теперь у нас есть базовая версияredux
, но это все еще не соответствует нашим потребностям. Наше обычное развитие бизнеса не будет таким простым, как в примере, написанном выше, тогда возникнет проблема:reducer
функции могут быть очень длинными, потому чтоaction
Тип будет очень. Это, безусловно, не способствует написанию и чтению кода.
Только представьте, что есть сто видов вашего бизнесаaction
Нужно разобраться, записать эти сотни ситуаций вreducer
В , мало того, что писать противно, так ещё и коллеги, которые поддерживают код в более поздний период, хотят убивать людей.
Поэтому лучше напишем отдельноreducer
, тогда правильноreducer
Объединить. есть у насcombineReducers
(а такжеredux
Название библиотеки осталось прежним) дебют~
combineReducers
Сначала нам нужно внести ясность:combineReducers
Просто функция полезности, как мы уже говорили ранее, она будет многократноreducer
сливаться в одинreducer
.combineReducers
то, что возвращаетсяreducer
, то есть это функция высшего порядка.
Мы все же объясним на примере, хотяredux
не надо миритьсяreact
сотрудничества, но с учетом егоreact
Сотрудничество является наиболее подходящим, здесь, сreact
Пример кода:
На этот раз мы добавили функцию счетчика (используяReact
Рефакторинг ===>to-redux2
):
//现在我们的 state 结构如下:
let state = {
theme: {
color: 'blue'
},
counter: {
number: 0
}
}
Очевидно, модификацию субъекта и счетчика можно разделить по разнымreducer
Собираться на сделку - лучший вариант.
store/reducers/counter.js
Отвечает за обработку состояния счетчика.
import { INCRENENT, DECREMENT } from '../action-types';
export default counter(state = {number: 0}, action) {
switch (action.type) {
case INCRENENT:
return {
...state,
number: state.number + action.number
}
case DECREMENT:
return {
...state,
number: state.number - action.number
}
default:
return state;
}
}
store/reducers/theme.js
Отвечает за обработку состояния, которое изменяет цвет темы.
import { CHANGE_COLOR } from '../action-types';
export default function theme(state = {color: 'blue'}, action) {
switch (action.type) {
case CHANGE_COLOR:
return {
...state,
color: action.color
}
default:
return state;
}
}
каждыйreducer
Отвечает только за управление глобальнымstate
часть отвечает. каждыйreducer
изstate
Параметры разные, соответствующие той части, которой он управляетstate
данные.
import counter from './counter';
import theme from './theme';
export default function appReducer(state={}, action) {
return {
theme: theme(state.theme, action),
counter: counter(state.counter, action)
}
}
appReducer
после слиянияreducer
, но когдаreducer
Когда их много, писать так громоздко, поэтому мы пишем инструментальную функцию для генерации такогоappReducer
Мы называем эту утилитую функцию какcombineReducers
.
Попробуем написать эту служебную функциюcombineReducers
:
Идеи:
-
combineReducers
вернутьreducer
-
combineReducers
Входных параметров несколькоreducer
составной объект - каждый
reducer
обрабатывать только глобальныеstate
та часть, за которую вы отвечаете
//reducers 是一个对象,属性值是每一个拆分的 reducer
export default function combineReducers(reducers) {
return function combination(state={}, action) {
//reducer 的返回值是新的 state
let newState = {};
for(var key in reducers) {
newState[key] = reducers[key](state[key], action);
}
return newState;
}
}
сынreducer
state
Значения по умолчанию. В этом примерном варианте осуществленияcreateStore
серединаdispatch({type:
@@redux/INIT${Math.random()}})
, при переходе наcreateStore
даcombineReducers(reducers)
функция, которая возвращаетcombination
.
согласно сstate=reducer(state,action)
,newState.theme=theme(undefined, action)
, newState.counter=counter(undefined, action)
,counter
а такжеtheme
два сынаreducer
вернуть отдельноnewState.theme
а такжеnewState.counter
начальное значение .
воспользоваться этимcombineReducers
можно переписатьstore/reducers/index.js
import counter from './counter';
import theme from './theme';
import { combineReducers } from '../redux';
//明显简洁了许多~
export default combineReducers({
counter,
theme
});
мы написалиcombineReducers
Хотя кажется, что он удовлетворяет наши потребности, у него есть недостаток, то есть он вернет новый.state
объект, что приводит к бессмысленному повторному рендерингу, когда данные не изменились. Следовательно, мы можем судить о данных и вернуться к исходному состоянию, когда данные не изменились.state
Вот и все.
Усовершенствованная версия CombineReducers
//代码中省略了一些判断,默认传递的参数均是符合要求的,有兴趣可以查看源码中对参数合法性的判断及处理
export default function combineReducers(reducers) {
return function combination(state={}, action) {
let nextState = {};
let hasChanged = false; //状态是否改变
for(let key in reducers) {
const previousStateForKey = state[key];
const nextStateForKey = reducers[key](previousStateForKey, action);
nextState[key] = nextStateForKey;
//只有所有的 nextStateForKey 均与 previousStateForKey 相等时,hasChanged 的值才是 false
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
//state 没有改变时,返回原对象
return hasChanged ? nextState : state;
}
}
applyMiddleware
официальная документацияпримерноapplyMiddleware
Объяснение очень понятное, и следующее также относится к содержанию официальной документации:
Ведение журнала
Рассмотрим небольшую проблему, если мы хотим иметь возможность печатать в консоли каждый раз, когда изменяется состояниеstate
, Так что же нам делать?
Самый простой:
//...
<button onClick={() => {
console.log(store.getState());
store.dispatch(actions.add(2));
}}>+</button>
//...
Конечно, этот метод определенно не рекомендуется, если мы 100 раз диспетчеризируем в нашем коде, мы не сможем написать это 100 раз. Так как он печатается при изменении состоянияstate
, то есть вreducer
распечатать перед звонкомstate
, reducer
вdispatch
вызван, то мы можем переопределитьstore.dispatch
способ, распечатать перед отправкойstate
Вот и все.
let store = createStore(reducer);
const next = store.dispatch; //next 的命令是为了和中间件的源码一致
store.dispatch = action => {
console.log(store.getState());
next(action);
}
информация о сбое
Предположим, нам нужно больше, чем просто печатьstate
, вам также необходимо распечатать сообщение об ошибке при отправке исключения.
const next = store.dispatch; //next 的命名是为了和中间件的源码一致
store.dispatch = action => {
try{
console.log(store.getState());
next(action);
} catct(err) {
console.error(err);
}
}
И если у нас есть другие потребности, то нам нужно остановить измененияstore.dispatch
метод, который, наконец, делает эту часть кода трудной для сопровождения.
Итак, нам нужно отделитьloggerMiddleware
а такжеexceptionMiddleware
.
let store = createStore(reducer);
const next = store.dispatch; //next 的命名是为了和中间件的源码一致
const loggerMiddleware = action => {
console.log(store.getState());
next(action);
}
const exceptionMiddleware = action => {
try{
loggerMiddleware(action);
}catch(err) {
console.error(err);
}
}
store.dispatch = exceptionMiddleware;
Мы знаем, что многиеmiddleware
предоставляются третьими лицами, поэтому необходимоdispatch
а такжеgetState
Он должен быть передан как параметр дляmiddleware
, далее переписано:
const loggerMiddleware = ({dispatch, getState}) => action => {
const next = dispatch;
console.log(getState());
next(action);
}
const exceptionMiddleware = ({dispatch, getState}) => action => {
try{
loggerMiddleware({dispatch, getState})(action);
}catch(err) {
console.error(err);
}
}
//使用
store.dispatch = exceptionMiddleware({dispatch, getState});
Теперь есть небольшая проблема,exceptionMiddleware
серединаloggerMiddleware
Это жестко закодировано, что определенно неразумно. Мы надеемся, что это параметр, чтобы его можно было использовать гибко. Нет причин толькоexceptionMiddleware
нужно быть гибким, независимо отloggerMiddleware
Далее читаем следующее:
const loggerMiddleware = ({dispatch, getState}) => next => action => {
console.log(getState());
return next(action);
}
const exceptionMiddleware = ({dispatch, getState}) => next => action => {
try{
return next(action);
}catch(err) {
console.error(err);
}
}
//使用
const next = store.dispatch;
const logger = loggerMiddleware({dispatch: store.dispatch, getState: store.getState});
store.dispatch = exceptionMiddleware(store)(logger(next));
middleware
Письменный формат.
middleware
получилnext()
изdispatch
функция и возвращаетdispatch
функция, возвращенная функция будет использоваться как следующаяmiddleware
изnext()
Но есть небольшая проблема, когда мидлваров много, код использующий мидлвары станет очень громоздким. с этой целью,redux
предоставилapplyMiddleware
функция инструмента.
Как мы видим выше, в итоге мы хотим изменитьdispatch
, поэтому нам нужно переписатьstore
, вернуть измененныйdispatch
После методаstore
.
Итак, можем уточнить следующие моменты:
-
applyMiddleware
Возвращаемое значениеstore
-
applyMiddleware
определенно принятьmiddleware
как параметр -
applyMiddleware
принять{dispatch, getState}
как ввод, ноredux
Входной параметр в исходном кодеcreateStore
а такжеcreateStore
Входные параметры, вдумайтесь, нет необходимости создавать внешнийstore
, ведь создано извнеstore
Кроме того, что он передается в функцию в качестве параметра, он не имеет никакого другого эффекта, лучше поставитьcreateStore
а такжеcreateStore
Передаются параметры, которые необходимо использовать.
//applyMiddleWare 返回 store.
const applyMiddleware = middleware => createStore => (...args) => {
let store = createStore(...args);
let middle = loggerMiddleware(store);
let dispatch = middle(store.dispatch); //新的dispatch方法
//返回一个新的store---重写了dispatch方法
return {
...store,
dispatch
}
}
Вышеупомянутоеmiddleware
Ситуация, но мы знаем,middleware
Или вполне вероятно, и больше мы в основном решаемmiddleware
вопрос, далее перефразированный.
//applyMiddleware 返回 store.
const applyMiddleware = (...middlewares) => createStore => (...args) => {
let store = createStore(...args);
let dispatch;
const middlewareAPI = {
getState: store.getstate,
dispatch: (...args) => dispatch(...args)
}
//传递修改后的 dispatch
let middles = middlewares.map(middleware => middleware(middlewareAPI));
//现在我们有多个 middleware,需要多次增强 dispatch
dispatch = middles.reduceRight((prev, current) => current(prev), store.dispatch);
return {
...store,
dispatch
}
}
Я не знаю, понимаете ли вы вышесказанноеmiddles.reduceRight
, ниже приводится подробное описание для вас:
/*三个中间件*/
let logger1 = ({dispatch, getState}) => dispatch => action => {
console.log('111');
dispatch(action);
console.log('444');
}
let logger2 = ({ dispatch, getState }) => dispatch => action => {
console.log('222');
dispatch(action);
console.log('555')
}
let logger3 = ({ dispatch, getState }) => dispatch => action => {
console.log('333');
dispatch(action);
console.log('666');
}
let middle1 = logger1({ dispatch, getState });
let middle2 = logger2({ dispatch, getState });
let middle3 = logger3({ dispatch, getState });
//applyMiddleware(logger1,logger2,logger3)(createStore)(reducer)
//如果直接替换
store.dispatch = middle1(middle2(middle3(store.dispatch)));
соблюдать вышеизложенноеmiddle1(middle2(middle3(store.dispatch)))
, если положитьmiddle1
,middle2
,middle3
Он рассматривается как каждый элемент массива.Если вы знакомы с API массива, вы можете подумать об этом.reduce
, если вы еще не знакомы сreduce
, вы можете просмотретьДокументация MDN.
//applyMiddleware(logger1,logger3,logger3)(createStore)(reducer)
//reduceRight 从右到左执行
middles.reduceRight((prev, current) => current(prev), store.dispatch);
//第一次 prev: store.dispatch current: middle3
//第二次 prev: middle3(store.dispatch) current: middle2
//第三次 prev: middle2(middle3(store.dispatch)) current: middle1
//结果 middle1(middle2(middle3(store.dispatch)))
читатьredux
Источник студентов, может знать исходный код снабженcompose
Функция иcompose
не используется в функцииreduceRight
, Вместо того, чтобы использоватьreduce
, поэтому код немного отличается. Но процесс анализа остается прежним.
compose.js
export default function compose(...funcs) {
//如果没有中间件
if (funcs.length === 0) {
return arg => arg
}
//中间件长度为1
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((prev, current) => (...args) => prev(current(...args)));
}
оreduce
Способ написания, рекомендуется, как указано вышеreduceRight
то же самое, сделать анализ
использоватьcompose
Перезапись служебной функцииapplyMiddleware
.
const applyMiddleware = (...middlewares) => createStore => (...args) => {
let store = createStore(...args);
let dispatch;
const middlewareAPI = {
getState: store.getstate,
dispatch: (...args) => dispatch(...args)
}
let middles = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...middles)(store.dispatch);
return {
...store,
dispatch
}
}
bindActionCreators
redux
также предоставляет намbindActionCreators
Инструментальная функция, код этой инструментальной функции очень прост, мы редко используем ее непосредственно в коде,react-redux
будет использоваться в . Вот кратко объясните:
//通常我们会这样编写我们的 actionCreator
import { INCRENENT, DECREMENT } from '../action-types';
const counter = {
add(number) {
return {
type: INCRENENT,
number
}
},
minus(number) {
return {
type: DECREMENT,
number
}
}
}
export default counter;
При отправке нам нужно написать:
import counter from 'xx/xx';
import store from 'xx/xx';
store.dispatch(counter.add());
Конечно, мы могли бы также написать наш actionCreator следующим образом:
function add(number) {
return {
type: INCRENENT,
number
}
}
При отправке нужно написать так:
store.dispatch(add(number));
Приведенные выше коды имеют одну общую черту, то есть все ониstore.dispatch
Отправляет действие. Таким образом, мы можем рассмотреть возможность написания функции, которая будетstore.dispatch
а такжеactionCreator
Связывать.
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args));
}
function bindActionCreators(actionCreator, dispatch) {
//actionCreators 可以是一个普通函数或者是一个对象
if(typeof actionCreator === 'function') {
//如果是函数,返回一个函数,调用时,dispatch 这个函数的返回值
bindActionCreator(actionCreator, dispatch);
}else if(typeof actionCreator === 'object') {
//如果是一个对象,那么对象的每一项都要都要返回 bindActionCreator
let boundActionCreators = {}
for(let key in actionCreator) {
boundActionCreators[key] = bindActionCreator(actionCreator[key], dispatch);
}
return boundActionCreators;
}
}
Когда используешь:
let counter = bindActionCreators(counter, store.dispatch);
//派发时
counter.add(number);
counter.minus(number);
Здесь это не кажется слишком упрощенным, и это будет проанализировано позже.react-redux
, мы объясним, зачем нам нужна эта служебная функция.
До сих пор нашredux
В основном написано. а такжеredux
По сравнению с исходным кодом все еще есть некоторые отличия, такие как некоторая проверка иcreateStore
который предоставилreplaceReducer
метод иcreateStore
Мы не упомянули второй и третий параметры , но вы можете понять это, взглянув немного на код, поэтому я не буду раскрывать их здесь один за другим.
Ссылка на ссылку
- Маленькая книга React.js
- Китайская документация REDUX
- Полностью понять редукцию (реализовать редукцию с нуля)