手書きReduxソース
35755 ワード
export { default as createStore } from "./createStore"
export { default as bindActionCreators } from "./bindActionCreators"
export { default as combineReducers } from "./combineReducers"
export { default as applyMiddleware } from "./applyMiddleware"
export { default as compose } from "./compose"
/**
*
* @param {*} length
*/
function getRandomString(length) {
return Math.random().toString(36).substr(2, length).split("").join(".")
}
export default {
INIT() {
return `@@redux/INIT${getRandomString(6)}`
},
UNKNOWN() {
return `@@redux/PROBE_UNKNOWN_ACTION${getRandomString(6)}`
}
}
/**
* plain-object
* @param {*} obj
*/
export default function isPlainObject(obj) {
if (typeof obj !== "object") {
return false;
}
return Object.getPrototypeOf(obj) === Object.prototype;
}
import ActionTypes from "./utils/ActionTypes"
import isPlainObject from "./utils/isPlainObject"
/**
* createStore
* @param {function} reducer reducer
* @param {any} defaultState
*/
export default function createStore(reducer, defaultState, enhanced) {
//enhanced applymiddleware
if (typeof defaultState === "function") {
//
enhanced = defaultState;
defaultState = undefined;
}
if (typeof enhanced === "function") {
// applyMiddleWare
return enhanced(createStore)(reducer, defaultState);
}
let currentReducer = reducer, // reducer
currentState = defaultState; //
const listeners = []; // ( )
function dispatch(action) {
// action
if (!isPlainObject(action)) {
throw new TypeError("action must be a plain object");
}
// action type
if (action.type === undefined) {
throw new TypeError("action must has a property of type");
}
currentState = currentReducer(currentState, action)
// ( )
for (const listener of listeners) {
listener();
}
}
function getState() {
return currentState;
}
/**
* ( )
*/
function subscribe(listener) {
listeners.push(listener); //
let isRemove = false;//
return function () {
if (isRemove) {
return;
}
// listener
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
isRemove = true;
}
}
// , action
dispatch({
type: ActionTypes.INIT()
})
return {
dispatch,
getState,
subscribe
}
}
import isPlainObject from "./utils/isPlainObject"
import ActionTypes from "./utils/ActionTypes"
function validateReducers(reducers) {
if (typeof reducers !== "object") {
throw new TypeError("reducers must be an object");
}
if (!isPlainObject(reducers)) {
throw new TypeError("reducers must be a plain object");
}
// reducer undefined
for (const key in reducers) {
if (reducers.hasOwnProperty(key)) {
const reducer = reducers[key];// reducer
// type
let state = reducer(undefined, {
type: ActionTypes.INIT()
})
if (state === undefined) {
throw new TypeError("reducers must not return undefined");
}
state = reducer(undefined, {
type: ActionTypes.UNKNOWN()
})
if (state === undefined) {
throw new TypeError("reducers must not return undefined");
}
}
}
}
export default function (reducers) {
//1.
validateReducers(reducers);
/**
* reducer
*/
return function (state = {}, action) {
const newState = {}; //
for (const key in reducers) {
if (reducers.hasOwnProperty(key)) {
const reducer = reducers[key];
newState[key] = reducer(state[key], action);
}
}
return newState; //
}
}
export default function (actionCreators, dispatch) {
if (typeof actionCreators === "function") {
return getAutoDispatchActionCreator(actionCreators, dispatch);
}
else if (typeof actionCreators === "object") {
const result = {}; //
for (const key in actionCreators) {
if (actionCreators.hasOwnProperty(key)) {
const actionCreator = actionCreators[key]; //
if (typeof actionCreator === "function") {
result[key] = getAutoDispatchActionCreator(actionCreator, dispatch);
}
}
}
return result;
}
else {
throw new TypeError("actionCreators must be an object or function which means action creator")
}
}
/**
* action
*/
function getAutoDispatchActionCreator(actionCreator, dispatch) {
return function (...args) {
const action = actionCreator(...args)
dispatch(action);
}
}
export default function compose(...funcs) {
if (funcs.length === 0) {
return args => args; // ,
}
else if (funcs.length === 1) {
//
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
// return function (...args) {
// let lastReturn = null; // ,
// for (let i = funcs.length - 1; i >= 0; i--) {
// const func = funcs[i];
// if (i === funcs.length - 1) {//
// lastReturn = func(...args)
// }
// else {
// lastReturn = func(lastReturn)
// }
// }
// return lastReturn;
// }
}
import compose from "./compose"
/**
*
* @param {...any} middlewares
*/
export default function (...middlewares) {
return function (createStore) { //
//
return function (reducer, defaultState) {
//
const store = createStore(reducer, defaultState);
let dispatch = () => { throw new Error(" dispatch") };
const simpleStore = {
getState: store.getState,
dispatch: store.dispatch
}
// dispatch
// , dispatch
const dispatchProducers = middlewares.map(mid => mid(simpleStore));
dispatch = compose(...dispatchProducers)(store.dispatch);
return {
...store,
dispatch
}
}
}
}