思维导图
demo1
import React, { Component } from "react";
import store from "../store";
export default class ReduxPage extends Component {
componentDidMount() {
// 告诉redux,一旦state变化(一旦执行dispatch函数),就执行的事件
this.unsubscribe = store.subscribe(() => {
this.forceUpdate();
});
}
componentWillUnmount() {
this.unsubscribe();
}
add = () => {
store.dispatch({ type: "ADD" });
};
minus = () => {
// setTimeout(() => {
// store.dispatch({ type: "MINUS" });
// }, 1000);
store.dispatch((dispatch, getState) => {
setTimeout(() => {
dispatch({ type: "MINUS" });
}, 1000);
});
};
promiseMinus = () => {
store.dispatch(
Promise.resolve({
type: "MINUS",
payload: 100,
})
);
};
render() {
return (
<div>
<h3>ReduxPage</h3>
<p>{store.getState().count}</p>
<button onClick={this.add}>add</button>
<button onClick={this.minus}>minus</button>
<button onClick={this.promiseMinus}>promiseMinus</button>
</div>
);
}
}
入口
import createStore from "./createStore";
import applyMiddleware from "./applyMiddleware";
import compose from "./compose";
import combineReducers from "./combineReducers";
import bindActionCreators from "./bindActionCreators";
export {
createStore,
applyMiddleware,
compose,
combineReducers,
bindActionCreators,
};
createStore
export default function createStore(reducer, enhancer) {
if (enhancer) {
return enhancer(createStore)(reducer);
}
let currentState;
let currentListeners = [];
function getState() {
return currentState;
}
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach((listener) => listener());
}
function subscribe(listener) {
currentListeners.push(listener);
return () => {
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
};
}
dispatch({ type: "ASASASASASA/REDUX" });
return {
getState,
dispatch,
subscribe,
};
}
- createStore里管理着currentState和currentListners这两个状态
- reucer在dispath里面进行调用,改变状态后将所有订阅的组件都强制刷新一下。这里全都刷新太浪费性能了,可以做优化
- 订阅之后记得卸载组件的时候要取消订阅
- 在createStore里手动执行一次dispatch,让currentState获取到初始状态
- enhancer是中间件,主要用来对createStore里的dispatch功能进行加强。简单版本的dispatch只能接受对象为参数,进行加强后可以接收函数作为参数,并且能处理异步
compose
export default function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args))
);
}
compose是一个utils函数,主要作用是对多个函数进行聚合,实现f1(f2(f3(...ars)))的函数调用功能
applyMiddle
import compose from "./compose";
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
const store = createStore(reducer);
let dispatch = store.dispatch;
// todo 加强dispatch
const midAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args),
};
const middlewareChain = middlewares.map((middleware) => middleware(midAPI));
// console.log(middlewareChain, "middleware chain");
// debugger;
// 加强版的dispatch
// 把所有的中间件的函数都执行了,同时还执行store.dispatch
dispatch = compose(...middlewareChain)(store.dispatch);
// console.log(dispatch, "dispatch middleware chain");
// debugger;
return {
...store,
// 加强版的dispatch
dispatch,
};
};
}
- 这是实现中间件原理的核心代码,虽然寥寥几行代码,但是功能强大。其主要作用就是通过中间件,加强dispatch功能,creteStore里的其它参数照抄
- 在midApi中,dispatch不能直接用store的dispatch,写成箭头函数形式的目的是为了能够在上下文中找到对应的dispatch
- middlewares.map的时候,所有中间件已经执行一次了,全都返回next=>action=>{...}的形式
- compose(...middlewareChain)(store.dispatch)返回的只有一个action=>{...}了,每一次action里的next都是下一个中间件返回的函数结果,这个时候dispatch=action=>{...}调用的时候就是一个洋葱模型的形式了
store
// import { createStore, applyMiddleware, combineReducers } from "redux";
import { createStore, applyMiddleware, combineReducers } from "../redux-nut";
// import thunk from "redux-thunk";
// import logger from "redux-logger";
import promise from "redux-promise";
// 定义了store修改规则
export function countReducer(state = 0, action) {
switch (action.type) {
case "ADD":
return state + 1;
case "MINUS":
return state - 1;
default:
return state;
}
}
// 创建store
const store = createStore(
// countReducer,
combineReducers({
count: countReducer,
}),
applyMiddleware(
// promise,
logger,
thunk
)
);
export default store;
function logger({ getState, dispatch }) {
return (next) => (action) => {
// debugger;
console.log("-----------------------"); //sy-log
console.log(action.type + "执行了"); //sy-log
const prevState = getState();
console.log("prev state", prevState); //sy-log
const returnValue = next(action);
// 等状态值修改之后,再执行getState,拿到了新的状态值
const nextState = getState();
console.log("next state", nextState); //sy-log
console.log("-----------------------"); //sy-log
return returnValue;
};
}
function thunk({ getState, dispatch }) {
return (next) => (action) => {
// debugger;
if (typeof action === "function") {
return action(dispatch, getState);
}
return next(action);
};
}
这里重点关注thunk和logger这两个中间件的实现,使用了高阶函数和函数柯里化的用法,与applyMiddle结合起来便能理解中间件的精髓。
combineRudecers
export default function combineReducers(reducers) {
// 返回一个总的reducer (prevState, action)=>nextState
return function combination(state = {}, action) {
let nextState = {};
let hasChanged = false;
for (const key in reducers) {
const reducer = reducers[key];
nextState[key] = reducer(state[key], action);
hasChanged = hasChanged || nextState[key] !== state[key];
}
// {a:1, b:1} {a:1}
hasChanged =
hasChanged || Object.keys(nextState).length !== Object.keys(state).length;
return hasChanged ? nextState : state;
};
}
主要实现思路是返回一个函数reducer,然后在这个函数reducer里对每个reducer进行遍历修改每一个reducer对应的state状态,在这里hasChange那里的逻辑作用不大。需要注意的是这里的实现不是把每个reducer里的switch进行合并。
demo2
import { Component } from "react";
// import { connect } from "react-redux";
import { connect } from "../react-redux-nut/";
// import { bindActionCreators } from "redux";
import { bindActionCreators } from "../redux-nut";
// HOC higer order Component,高阶组件:是个函数,接受组件作为参数,返回新的组件
export default connect(
// mapStateToProps,
// (state, ownProps) => {
// console.log("ownProps", ownProps); //sy-log
// return state;
// }
({ count }) => ({ count }),
// mapDispatchToProps function|object
// (dispatch) => {
// let creators = {
// add: () => ({ type: "ADD" }),
// minus: () => ({ type: "MINUS" }),
// };
// creators = bindActionCreators(creators, dispatch);
// return { dispatch, ...creators };
// }
{
add: () => ({ type: "ADD" }),
minus: () => ({ type: "MINUS" }),
}
)(
class ReactReduxPage extends Component {
render() {
console.log("props", this.props); //sy-log
const { count, dispatch, add, minus } = this.props;
return (
<div>
<h3>ReactReduxPage</h3>
<button
onClick={() => {
dispatch({ type: "ADD" });
}}
>
dispatch:{count}
</button>
<button onClick={add}>add: {count}</button>
</div>
);
}
}
);
react-redux
// Context传值 跨组件层级传递数据
import React, {
useCallback,
useContext,
useEffect,
useLayoutEffect,
useReducer,
useState,
useSyncExternalStore,
} from "react";
import { bindActionCreators } from "../redux-nut";
// ! 1. 创建context对象
const Context = React.createContext();
// ! 2. Provider组件传递value (store)
export function Provider({ store, children }) {
return <Context.Provider value={store}>{children}</Context.Provider>;
}
// ! 3.后代消费Provider传递下来的value
// * contextType 只能用在类组件,只能订阅单一的context来源
// * useContext 只能用在类组件或者自定义Hook中
// * Consumer 没有组件限制,注意使用方式
//返回一个新的函数式组件(props) => {...}
export const connect =
(mapStateToProps, mapDispatchToProps) => (WrappedComponent) => (props) => {
const store = useContext(Context);
const { getState, dispatch, subscribe } = store;
// const stateProps = mapStateToProps(getState());
let dispatchProps = { dispatch };
if (typeof mapDispatchToProps === "function") {
dispatchProps = mapDispatchToProps(dispatch);
} else if (typeof mapDispatchToProps === "object") {
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
}
// const [, forceUpdate] = useReducer((x) => x + 1, 0);
const forceUpdate = useForceUpdate();
// // DOMeffect
// useLayoutEffect(() => {
// const unsubscribe = subscribe(() => {
// forceUpdate();
// });
// return () => {
// unsubscribe();
// };
// }, [subscribe]);
const state = useSyncExternalStore(() => {
subscribe(forceUpdate);
}, getState);
// console.log("checked", state === getState()); //sy-log
const stateProps = mapStateToProps(state);
return <WrappedComponent {...props} {...stateProps} {...dispatchProps} />;
};
function useForceUpdate() {
const [state, setState] = useState(0);
const update = useCallback(() => {
setState((prev) => prev + 1);
}, []);
return update;
}
export function useSelector(selector) {
const store = useContext(Context);
const { getState, subscribe } = store;
// const selectedState = selector(getState());
const forceUpdate = useForceUpdate();
// // DOMeffect
// useLayoutEffect(() => {
// const unsubscribe = subscribe(() => {
// forceUpdate();
// });
// return () => {
// unsubscribe();
// };
// }, [subscribe]);
const state = useSyncExternalStore(() => {
subscribe(forceUpdate);
}, getState);
const selectedState = selector(state);
return selectedState;
}
export function useDispatch() {
const store = useContext(Context);
const { dispatch } = store;
return dispatch;
}
- 从上面redux使用的案例可以看到,每次使用都得自己订阅强刷新,这样页面多的时候就会显得很冗余,react-redux就是把这些共用的订阅过程进行一个合并,由react-reux来完成这些操作,而页面只需要使用state及dispatch即可。
- 对于类组件,核心实现是connect函数,这是一个高阶组件,传入一个组件,返回一个包装后的新组件,在包装的过程中通过context获取到父组件传下来的store,通过使用getState、dispatch、subscribe这三个api来对状态进行管理和页面刷新
- 函数时组件的核心实现是useSelector和useDispatch这两个api,主要也是通过context获取到父组件传下来的store,然后通过getState、dispatch、subscribe这三个api来对状态进行管理和页面刷新