createStore
This createStore function needs to return an object containing the following attributes:
dispatch
: Distribute actions to change datagetState
: Gets the state in the current repositorysubscribe
: Registers a listener and fires after the action is issued. And returns a function that cancels listeningreplaceReducer
Replace the current reducer in the repositorySymbol("observable")
: built-in function to implement observer mode (not implemented yet)
Code implementation:
/** * create repository *@param {function} reducer
* @param {any} DefaultState specifies the defaultState */
export default function createStore(reducer, defaultState) {
// Simply verify that reducer is a function
if (typeofreducer ! = ='function') {
throw new TypeError(`The first parameter reducer must be a function`)}let currentReducer = reducer Reducer is currently in use
let currentState = defaultState // Current status value
const subscribers = [] // Array of listeners
function dispatch(action) {
// Verify the action type
if(! _isPlainObject(action)) {throw new TypeError(`The required parameter action must be a "Plain Object"`)}if (action.type === undefined) {
throw new TypeError(`The required parameter action must have a property of "type"`)
}
currentState = currentReducer(currentState, action)
// Execute all listeners
subscribers.forEach(subscriber= > {
subscriber()
})
}
function getState() {
return currentState
}
// Add a listener
function subscribe(subscriber) {
subscribers.push(subscriber)
let isRemoved = false // Determine if it has been removed
return () = > {
if (isRemoved) return
const index = subscribers.indexOf(subscriber)
subscribers.splice(index, 1)
isRemoved = true}}function replaceReducer(newReducer) {
// Simply verify that reducer is a function
if (typeofnewReducer ! = ='function') {
throw new TypeError(`The first parameter reducer must be a function`)
}
currentReducer = newReducer
}
// When the warehouse is created, a dispatch action needs to be called to complete state initialization
dispatch({
type: `@@redux/INIT${_getRandomStr(7)}`
})
return {
dispatch,
getState,
subscribe,
replaceReducer
}
}
Copy the code
Auxiliary functions involved:
/** * Check whether this object is a flat object *@param {Object} O Target object *@returns boolean* /
function _isPlainObject(o) {
if (typeofo ! = ='object') {
return false
}
return Object.getPrototypeOf(o) === Object.prototype
}
/** * returns a string of the specified length, for example: 'f.A. 4.d.7' *@param {number} Len string length */
function _getRandomStr(len = 7) {
// Convert to base 36, i.e., 26 letters + 10 digits
return Math.random().toString(36).substr(2, len).split(' ').join('. ')}Copy the code
bindActionCreators
By passing two arguments :(1) action creates the function or the object that action creates the function; (2) the dispatch function
To get the enhanced action create function or the action create function object
/** * enhances the action creation function *@param {*} actionCreators
* @param {*} dispatch
*/
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return _getAutoDispatchActionCreator(actionCreators, dispatch)
} else if( actionCreators ! = =null &&
typeof actionCreators === 'object'&&!Array.isArray(actionCreators)
) {
const res = {}
for (const key in actionCreators) {
if (Object.hasOwnProperty.call(actionCreators, key)) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
res[key] = _getAutoDispatchActionCreator(actionCreator, dispatch)
}
}
}
return res
} else {
throw new TypeError(`The first parameter of actionCreators must be an "object" or "function" which means action creator`)}}/** * get the creation function that automatically distributes the action *@param {*} actionCreators
* @param {*} dispatch
*/
function _getAutoDispatchActionCreator(actionCreators, dispatch) {
return (. args) = > {
constaction = actionCreators(... args) dispatch(action) } }Copy the code
combineReducers
Function: Assemble reducers and return a Reducer. Data is represented by an object, and the attribute name of the object is consistent with the parameter object passed
When it is called to merge the reducers, each Reducer will distribute two special types of actions to ensure that the returned state is not undefined
The implementation is as follows:
/** * Merge reducers *@param {Object}} reducers
* @return {Funtion} reducer* /
export default function combineReducers(reducers) {
_validateReducers(reducers)
return function (state = {}, action) {
const newState = {}
for (const key in reducers) {
if (Object.hasOwnProperty.call(reducers, key)) {
const reducer = reducers[key]
newState[key] = reducer(state[key], action)
}
}
return newState
}
}
/** * Verify reducers *@param {*} reducers* /
function _validateReducers(reducers) {
if (
typeofreducers ! = ='object' ||
reducers === null ||
Array.isArray(reducers)
) {
throw new TypeError(`reducers must be an "object"`)}if(! isPlainObject(reducers)) {throw new TypeError(`reducers must be a "plain object"`)}Verify that each reducer return result is undefined
for (const key in reducers) {
if (Object.hasOwnProperty.call(reducers, key)) {
const reducer = reducers[key]
let state = reducer(undefined, {
type: `@@redux/INIT${_getRandomStr(7)}`
})
if (state === undefined) {
throw new TypeError(`reducer returns state cannot be undefined`)
}
state = reducer(undefined, {
type: `@@redux/PROBE_UNKNOWN_ACTION${_getRandomStr(7)}`
})
if (state === undefined) {
throw new TypeError(`reducer returns state cannot be undefined`)}}}}Copy the code