Implement Redux one by one

Implement Redux one by one

Preface

I remember that when I first came into contact with state management, the first one was redux. It was all brand new terms at the time:

reducer, state, dispatch, middleware, store
Wait, I understand a state, because react has the concept of state.

Let s start with state management and implement it step by step

redux
.

State manager

Simple state management

redux is a state manager. What is status? In simple terms, state is data, such as the count of a counter.

let state = { count : 1 } //use state Console .log (COUNT) //modify the status state.count = 2 duplicated code

Very good, we have realized one of the simplest state modification and use. But there are 3 obvious problems above:

  • After the count is modified, the place where count is used cannot be notified.
  • state.count is a global variable, count can be modified anywhere. This is dangerous.
  • This state management can only manage count, not universal.

We can use publish-subscribe model and encapsulation to solve this problem.

const createStore = function (initState) { let state = initState; let listeners = []; // function subscribe(listener) { listeners.push(listener); // return function unsubscribe() { const index = listeners.indexOf(listener) listeners.splice(index, 1) } } // function dispatch(newState) { state = newState; /* */ for (let i = 0; i < listeners.length; i++) { const listener = listeners[i]; listener(); } } //The only way to externally access state function getState () { return state; } return { subscribe, dispatch, getState } } Copy code

The minimalist version of state management has been implemented, let's use this state management to give it a try.

let initState = { counter : { count : 0 } } const store = createStore(initState); store.subscribe( () => { let state = store.getState(); console.log(state.counter.count); }); store.dispatch({ counter : { count : 1 } }); Copy code

Unconsciously, we have implemented redux

createStore
,provided
subsrcibe
,
dispatch
,
getState
3.APIs.

Predictable state management

We use the above state manager to implement an increment and decrement counter.

let initState = { count : 0 } let store = createStore(initState); store.subscribe( () => { let state = store.getState(); console .log(state.count); }); /*Increase*/ store.dispatch({ count : store.getState().count + 1 }); /*Decrease*/ store.dispatch({ count : store.getState().count- 1 }); /*I want to change whatever you want*/ store.dispatch({ count : 'abc' }); Copy code

You must have found the problem,

count
Was changed to a string
abc
, Because we have no restrictions on the modification of count, anyone can modify it anywhere.

We need to be constrained. Unplanned count modification is not allowed. We only allow count to increase and decrease in two ways!

Then we solve this problem in two steps:

  1. Develop a state modification plan and tell the store what my modification plan is. Use plans to make the state predictable
  2. modify
    store.dispatch
    Method, tell it to modify the state according to our plan.

Let's set up one

reducer
Function, receiving the current state, and a
action
, Return the new state after passing.

/* NOTE: action = {type: '' , other: ''}, action must have a type attribute */ function the reducer ( State, Action ) { Switch (action.type) { Case 'the INCREMENT' : return { ...state, count : state.count + 1 } Case 'DECREMENT push-' : return { ...state, count : state.count- 1 } default : return state; } } Copy code

We tell the store about this plan and call

store.dispatch
Change the state according to my plan.

/* reducer*/ const createStore = function (reducer, initState) { let state = initState; let listeners = []; function subscribe(listener) { listeners.push(listener); // return function unsubscribe() { const index = listeners.indexOf(listener) listeners.splice(index, 1) } } function dispatch(action) { /* state*/ state = reducer(state, action); for ( let i = 0 ; i <listeners.length; i++) { const listener = listeners[i]; listener(); } } function getState () { return state; } return { subscribe, dispatch, getState } } Copy code

Let's try to use the new createStore to achieve self-increment and self-decrement

let initState = { count : 0 } /*Put reducer function*/ let store = createStore(reducer, initState); store.subscribe( () => { let state = store.getState(); console .log(state.count); }); /*Increase*/ store.dispatch({ type : 'INCREMENT' }); /*Decrease*/ store.dispatch({ type : 'DECREMENT' }); /*I think it will be invalid to change the unplanned changes casually! */ store.dispatch({ count : 'abc' }); Copy code

reducer
I.e. planning function,
reducer
Make our state predictable.

Multi-file collaboration

reducer

reducer
state state state state

reducer
reducer
reducer

state counter info

let state = { counter: { count: 0 }, info: { name: ' reducer' } }

reducer

/* counterReducer, reducer */ /* counterReducer state state.counter*/ function counterReducer(state, action) { switch (action.type) { case 'INCREMENT': return { count: state.count + 1 } default: return state; } }
/*InfoReducer reducer*/ /* InfoReducer state state.info*/ function InfoReducer(state, action) { switch (action.type) { case 'SET_NAME': return { ...state, name: action.name } default: return state; } }

reducer
,
combineReducers
reducer
reducer
. :

const reducer = combineReducers({ counter: counterReducer, info: InfoReducer });

combineReducers
:

function combineReducers(reducers) { return function combination(state = {}, action) { const nextState = {}; for (let [key, reducer] of Object.entries(reducers)) { const prevState = state[key]; // reducer state nextState[key] = reducer(prevState, action); } return nextState; }; }

combineReducers
:

const reducer = combineReducers({ counter: counterReducer, info: InfoReducer }); let initState = { counter: { count: 0 }, info: { name: ' combineReducers', } } let store = createStore(reducer, initState);

state

reducer combineReducers state state state reducer

:

/* counter state reducer */ let initState = { count: 0 } function counterReducer(state, action) { /* state */ if (!state) { state = initState; } switch (action.type) { case 'INCREMENT': return { count: state.count + 1 } default: return state; } }

!

const createStore = function (initState) { let state = initState; let listeners = []; // function subscribe(listener) { listeners.push(listener); } // function dispatch(newState) { state = newState; /* */ for (let i = 0; i < listeners.length; i++) { const listener = listeners[i]; listener(); } } // type dispatch({ type: Symbol() }) // state function getState() { return state; } return { subscribe, dispatch, getState } }

?

  1. createStore type action state = reducer(state, action)
  2. action.type reducer default state state

middleware

middleware redux . . :

dispatch dispatch

state state state store.dispatch :

const store = createStore(reducer); const next = store.dispatch; // store.dispatch store.dispatch = (action) => { console.log('this state', store.getState()); console.log('action', action); next(action); console.log('next state', store.getState()); }

dispatch

const store = createStore(reducer); const next = store.dispatch; store.dispatch = (action) => { try { next(action); } catch (err) { // dispatch console.error(' : ', err) } }

, !

store.dispatch = (action) => { try { console.log('this state', store.getState()); console.log('action', action); next(action); console.log('next state', store.getState()); } catch (err) { console.error(' : ', err) } }

?

dispatch
? ! 200 ?
dispatch
, ...

  1. loggerMiddleware
const store = createStore(reducer); const next = store.dispatch; const loggerMiddleware = (action) => { console.log('this state', store.getState()); console.log('action', action); next(action); console.log('next state', store.getState()); } store.dispatch = (action) => { try { loggerMiddleware(action); } catch (err) { console.error(' : ', err) } }
  1. exceptionMiddleware
const exceptionMiddleware = (action) => { try { /*next(action)*/ loggerMiddleware(action); } catch (err) { console.error(' : ', err) } } store.dispatch = exceptionMiddleware;
  1. exceptionMiddleware loggerMiddleware next(action)
const exceptionMiddleware = (next) => (action) => { try { /*loggerMiddleware(action);*/ next(action); } catch (err) { console.error(' : ', err) } } /*loggerMiddleware */ store.dispatch = exceptionMiddleware(loggerMiddleware);
  1. loggerMiddleware next store.dispatch loggerMiddleware next
const loggerMiddleware = (next) => (action) => { console.log('this state', store.getState()); console.log('action', action); next(action); console.log('next state', store.getState()); }

.

const store = createStore(reducer); const next = store.dispatch; const loggerMiddleware = (next) => (action) => { console.log('this state', store.getState()); console.log('action', action); next(action); console.log('next state', store.getState()); } const exceptionMiddleware = (next) => (action) => { try { next(action); } catch (err) { console.error(' : ', err) } } store.dispatch = exceptionMiddleware(loggerMiddleware(next));

loggerMiddleware.js exceptionMiddleware.js

loggerMiddleware store store !

const store = createStore(reducer); const next = store.dispatch; const loggerMiddleware = (store) => (next) => (action) => { console.log('this state', store.getState()); console.log('action', action); next(action); console.log('next state', store.getState()); } const exceptionMiddleware = (store) => (next) => (action) => { try { next(action); } catch (err) { console.error(' : ', err) } } const logger = loggerMiddleware(store); const exception = exceptionMiddleware(store); store.dispatch = exception(logger(next));

applyMiddleware

import loggerMiddleware from './middlewares/loggerMiddleware'; import exceptionMiddleware from './middlewares/exceptionMiddleware'; ... const store = createStore(reducer); const next = store.dispatch; const logger = loggerMiddleware(store); const exception = exceptionMiddleware(store); store.dispatch = exception(logger(next));

createStore .

/* createStore createStore*/ const newCreateStore = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware)(createStore); /* dispatch store*/ const store = newCreateStore(reducer);

applyMiddleware

//compose function compose(...funcs) { if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } function applyMiddleware(...middlewares) { return function rewriteCreateStoreFunc(oldCreateStore) { return function newCreateStore(reducer, initState) { // store const store = oldCreateStore(reducer, initState); /* middleware store const logger = loggerMiddleware(store);*/ /* const chain = [exception, time, logger]*/ const chain = middlewares.map((middleware) => middleware({ getState: store.getState // , store })); /* exception(time((logger(dispatch))))*/ store.dispatch = compose(...chain)(store.dispatch); return store; }; }; }

createStore

/* createStore*/ import { createStore } from './redux'; const store = createStore(reducer, initState); /* createStore*/ const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware); const newCreateStore = rewriteCreateStoreFunc(createStore); const store = newCreateStore(reducer, initState);

createStore

const createStore = (reducer, initState, rewriteCreateStoreFunc) => { /* rewriteCreateStoreFunc createStore */ if(rewriteCreateStoreFunc){ const newCreateStore = rewriteCreateStoreFunc(createStore); return newCreateStore(reducer, initState); } /* */ ... }

const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware); const store = createStore(reducer, initState, rewriteCreateStoreFunc);

redux .

replaceReducer

reducer reducer reducer reducer

const createStore = function (reducer, initState) { ... function replaceReducer(nextReducer) { reducer = nextReducer /* state reducer state */ dispatch({ type: Symbol() }) } ... return { ... replaceReducer } }

bindActionCreators

bindActionCreators react-redux connect

dispatch actionCreator redux

dispatch actionCreator

const reducer = combineReducers({ counter: counterReducer, info: infoReducer }); const store = createStore(reducer); /* action actionCreator*/ function increment() { return { type: 'INCREMENT' } } function setName(name) { return { type: 'SET_NAME', name: name } } const actions = { increment: function () { return store.dispatch(increment.apply(this, arguments)) }, setName: function () { return store.dispatch(setName.apply(this, arguments)) } } /* actions */ /* dispatch actionCreator */ actions.increment(); /* */ actions.setName(' !!!'); /* info.name*/

actions

const actions = bindActionCreators({ increment, setName }, store.dispatch)

bindActionCreators
,

export default function bindActionCreator(actionCreator, dispatch) { if (typeof actionCreator === "function") { return () => dispatch(actionCreator.apply(this, arguments)); } if (typeof actionCreator !== "object" || actionCreator === null) { throw new Error(); } const boundActionCreators = {}; for (const [key, actionCreator] of actionCreator) { boundActionCreators[key] = () => dispatch(actionCreator.apply(this, arguments)); } return boundActionCreators; }

github