react 是一个用于用户界面构建的库,提供了 createContext
来进行状态管理,当复杂项目中 context 就捉襟见肘了,需要使用合理的工具来管理状态。
a. 父子组件通过 props 传递数据, 当属性跨层级传递时不是很方便。
function Parent() {
const a = 1;
return (
<div>
<Child a={a} />
</div>
);
}
function Child(props) {
console.log(props); //{a: 1}
return <div>{props.a}</div>;
}
b.createContext
react提供了createContext,创建上下文context来实现属性的跨层级传递。
React.createContext()
方法是React提供的一个用于创建上下文的方法。通过创建上下文,可以在组件之间共享数据,避免了通过 props 一层层地传递数据的繁琐过程。使用上下文,可以更方便地在组件树中的任何地方访问共享的数据。
import { createContext } from "react";
//使用React.createContext()方法创建上下文
const MyContext = createContext();
function Text() {
const obj = { a: 1, b: false };
return (
// 需要注意的是,Consumer组件必须在Provider组件的子组件中使用,否则无法获取到上下文中的值。
//组件中使用Provider组件来提供共享的数据
<MyContext.Provider value={obj}>
<Child />
</MyContext.Provider>
);
}
function Child() {
console.log(MyContext);
/**
* $$typeof: Symbol(react.context)
* Consumer:{$$typeof: Symbol(react.context), _context: {…}, …}
* Provider: {$$typeof: Symbol(react.provider), _context: {…}}
* _currentValue: {a: 1, b: false}
*/
//在Consumer组件中,使用一个函数作为子元素,并将上下文中的数据作为参数传递给这个函数。在这个函数中,可以使用上下文中的数据
return (
<div>
<p>test context</p>
<MyContext.Consumer>
{(value) => {
return <div>{value.a}</div>;
}}
</MyContext.Consumer>
</div>
);
}
c. redux 中文官网。
为了我们开发方便,可以使用redux-devtools
来查看redux状态。 Redux中,处理异步操作需要使用中间件,如redux-thunk或redux-saga,redux-promise等中间件
<!-- 创建store -->
let store = createStore(combinedReducer);
//action
export const ADD1 = "ADD1";
export const MINUS1 = "MINUS1";
import { ADD1, MINUS1 } from "../action-types";
function add1() {
//actionCreator 它是一个创建action的函数
return { type: ADD1 };
}
//reducers
import { ADD1, MINUS1, DOUBLE } from "../action-types";
let initState = { number: 0 };
const reducer = (state = initState, action) => {
switch (action.type) {
case ADD1:
return { number: state.number + 1 };
case MINUS1:
return { number: state.number - 1 };
case DOUBLE:
return { number: state.number * 2 };
default:
return state;
}
};
// 使用combineReducers方法合并reducer
let combinedReducer = combineReducers({
counter1,
counter2,
});
// 在组件中的使用
import actionCreators from "../store/actionCreators/counter1";
import { connect } from "react-redux";
class Counter1 extends React.Component {
render() {
return (
<div>
<p>{this.props.number}</p>
<button onClick={this.props.add1}>+</button>
</div>
);
}
}
//把仓库中的状态映射为组件的属性对象 仓库到组件的输出
const mapStateToProps = (state) => state.counter1;
//const mapDispatchToProps = dispatch => bindActionCreators(actionCreators, dispatch)
export default connect(
mapStateToProps,
actionCreators //组件的输出,在组件里派发动作,修改仓库
)(Counter1);
d. redux-tookit 用于简化Redux开发的工具集。它提供了一组用于处理常见Redux任务的工具和API,使得开发者可以更快、更简单地编写Redux代码。英文官网:redux-toolkit.js.org/
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./reducers";
//configureStore函数,用于创建Redux store。这个函数封装了常见的Redux配置,包括合并reducer、应用中间件和DevTools等。使用configureStore函数可以减少配置代码的编写量,并提供了一些默认的配置选项
const store = configureStore({
reducer: rootReducer,
});
const initialState = { value: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case "increment":
return { ...state, value: state.value + 1 };
case "decrement":
return { ...state, value: state.value - 1 };
case "incrementByAmount":
return { ...state, value: state.value + action.payload };
default:
return state;
}
}
function increment(amount: number) {
return {
type: INCREMENT,
payload: amount,
};
}
const action = increment(3);
//createSlice:Redux Toolkit提供了一个createSlice函数,用于创建Redux的slice。slice是一个包含了reducer和action的对象,用于定义和处理Redux的一部分状态。使用createSlice函数可以更简洁地定义slice,并自动生成对应的reducer和action。
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment(state) {
state.value++;
},
decrement(state) {
state.value--;
},
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload;
},
},
});
e. dva dva
// 创建应用
const app = dva();
// 注册 Model
app.model({
namespace: "count",
state: 0,
reducers: {
add(state) {
return state + 1;
},
},
effects: {
//Effect是一个Generator函数,用于处理异步的副作用操作。在Dva中,每个Model可以定义一个或多个effect函数,用于处理异步的操作,如发送网络请求、访问浏览器缓存等。Effect函数通过使用Dva提供的一些内置的effect函数,如call、put、select等,来处理异步操作。
*addAfter1Second(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: "add" });
},
},
});
// 注册视图
app.router(() => <ConnectedApp />);
// 启动应用
app.start("#root");
f. rematch
基于Redux的轻量级框架,用于简化Redux应用程序的开发。它提供了一种简单的方式来定义和管理Redux的模型(Model),并提供了一些内置的功能和约定,使得开发者可以更快、更简单地编写Redux代码。
Rematch使用了redux-saga来处理异步操作,使得处理异步操作更简单和直观。 Rematch提供了一个插件系统,可以通过插件来扩展和定制Rematch的功能。插件可以用于添加中间件、增强Model、添加额外的功能等。这使得开发者可以根据实际需求来选择和使用插件,从而更灵活地定制Rematch的功能。
// 在Model中,可以定义state属性来表示状态的初始值。reducers属性用于定义同步的状态更新,effects属性用于定义异步的副作用操作。
// 过使用connect函数,可以将组件与Rematch store连接起来。mapState函数用于将状态映射到组件的props中,mapDispatch函数用于将action函数映射到组件的props中
export const countModel = {
state: { counter: 0 }, // initial state
reducers: {
add: (state, payload) => {
return {
...state,
counter: state.counter + payload,
};
},
},
effects: {
async loadData(payload, rootState) {
const response = await fetch(`http://xxx.com/${payload}`);
const data = await response.json();
this.add(data); // dispatch action to a local reducer
},
},
};
// 创建一个Rematch store:
// 在init函数中,可以通过models属性来定义一个或多个Model。每个Model包含了reducers、effects和selectors等属性,用于定义和管理状态和副作用。
import { init } from "@rematch/core";
const store = init({
models: {
// 定义Model
},
});
还有其他状态管理工具如 MobX,Zustand,React Query,感兴趣的可以去了解下。
版本redux@4.2.0
export {
createStore,
legacy_createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes,
};
// __DO_NOT_USE__ActionTypes
// 这是redux内部的默认action,我们在设置自己的action的时候,不要和他们重复
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`,
REPLACE: `@@redux/REPLACE${randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`,
};
export function createStore(reducer, preloadedState, enhancer) {
if (
(typeof preloadedState === "function" && typeof enhancer === "function") ||
(typeof enhancer === "function" && typeof arguments[3] === "function")
) {
// 这里进行参数校验
}
if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
enhancer = preloadedState;
preloadedState = undefined;
}
if (typeof enhancer !== "undefined") {
if (typeof enhancer !== "function") {
throw new Error(
`Expected the enhancer to be a function. Instead, received: '${kindOf(
enhancer
)}'`
);
}
// 如果有enhancer则在enhancer内部初始化store
return enhancer(createStore)(reducer, preloadedState);
}
// reducer是纯函数,在函数内部修改state。
if (typeof reducer !== "function") {
throw new Error(
`Expected the root reducer to be a function. Instead, received: '${kindOf(
reducer
)}'`
);
}
let currentReducer = reducer;
let currentState = preloadedState;
let currentListeners = [];
let nextListeners = currentListeners;
let isDispatching = false;
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
}
function getState() {
return currentState;
}
// subcribe是我们订阅事件的地方,subcribe返回一个卸载函数。
// 订阅状态变化事件,当状态发生改变后执行所有的监听函数、
// 创建Redux store时,可以通过调用store.subscribe()方法来订阅状态的变化。该方法接受一个回调函数作为参数,这个回调函数会在状态发生变化时被调用
function subscribe(listener) {
let isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
// 如果不再需要监听状态的变化,应该及时取消订阅,以避免不必要的性能消耗。可以通过调用返回的取消订阅函数来取消订阅,例如:const unsubscribe = store.subscribe(callback),然后在不需要监听时调用unsubscribe()即可。
return function unsubscribe() {
if (!isSubscribed) {
return;
}
isSubscribed = false;
ensureCanMutateNextListeners();
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
currentListeners = null;
};
}
// dispatch是一个用于触发状态变化的方法。它是Redux中唯一能够改变状态的方式。
// 通过调用store.dispatch()方法来触发状态的变化。该方法接受一个action对象作为参数,这个action对象描述了状态变化的类型和相关的数据。
function dispatch(action) {
if (!isPlainObject(action)) {
// 判断action的类型
}
if (typeof action.type === "undefined") {
// 判断action的类型
}
// 调用dispatch方法时,Redux会根据action对象的类型来执行相应的reducer函数。reducer函数是一个纯函数,它接受当前的状态和action对象作为参数,并返回一个新的状态。
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 在这里执行subscribe订阅的方法,响应式更新
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
return action;
}
// replaceReducer是一个用于替换当前reducer的方法。它允许我们在运行时动态地替换Redux store中的reducer函数。
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: ActionTypes.REPLACE });
}
//当创建一个存储时,会调度一个“INIT”操作,以便reducer返回其初始状态。这有效地填充初始状态树。
dispatch({ type: ActionTypes.INIT });
return {
dispatch,
subscribe,
getState,
replaceReducer,
};
}
export const legacy_createStore = createStore;
bindActionCreators 用于绑定action creators到dispatch的方法,简化在组件中使用action creators的过程. 返回一个与原始action creators具有相同键值对的对象,但是每个action creator都会被自动调用dispatch函数进行包装。
function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
// 返回调用dispatch函数的回调
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments))
}
}
applyMiddleware
是一个用于redux使用中间件的方法。允许我们在Redux的数据流中插入自定义的逻辑,以处理异步操作、日志记录、错误处理等。 创建store时,可以通过调用applyMiddleware()
方法来应用中间件。该方法接受一个或多个中间件函数作为参数,并返回一个增强后的store创建函数
中间件函数是一个接受store的dispatch函数作为参数的函数。在dispatch函数被调用之前或之后执行一些额外的逻辑。中间件函数可以访问store的getState方法来获取当前的状态,也可以调用dispatch方法来触发下一个中间件或reducer函数。 我们常用的redux中间件有:redux-thunk
中间件来处理异步操作,redux-logger
中间件来记录日志,redux-promise
中间件来处理Promise对象,redux-saga
处理异步操作等。
function applyMiddleware(...middlewares) {
return (createStore) => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args),
}
const chain = middlewares.map((middleware) => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch,
}
}
}
// 中间件函数的顺序非常重要。它们会按照顺序依次执行,因此前一个中间件的输出会成为下一个中间件的输入。在编写中间件时,我们需要确保它们的顺序是正确的,以便实现预期的逻辑。
// compose是一个用于组合函数的方法。允许我们将多个函数按照从右到左的顺序依次执行,并将每个函数的输出作为下一个函数的输入。
// 调用compose(f, g, h)时,它会返回一个新的函数,这个新的函数等价于f(g(h(...args)))。也就是说,它会将参数args依次传递给h、g和f,并将每个函数的输出作为下一个函数的输入。
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)))
}
//日志中间件 中间件的格式是固定的
function logger({ getState, dispatch }) {
return function (next) {
return function (action) {//此方法就是我们改造后的dispatch方法
console.log('老状态', getState());
next(action);//调用原始的dispatch方法,传入动作action,发给仓库,仓库里会调用reducer计算新的状态
console.log('新状态', getState());
return action;
}
}
}
function promise({ getState, dispatch }) {
return function (next) {
return function (action) {//此方法就是我们改造后的dispatch方法
if (action.then && typeof action.then === 'function') {
action.then(dispatch)
} else {
next(action);
}
}
}
}
function thunk({ getState, dispatch }) {
return function (next) {
return function (action) {//此方法就是我们改造后的dispatch方法
if (typeof action === 'function') {
//把新的dispatch传递给了函数,这样就可以在函数里派发动作
return action(getState, dispatch);
}
return next(action);
}
}
}
主要分为两部分:第一部分简单介绍了react常用的状态管理工具,第二部分分析了redux的源码和中间件。后面有时间介绍下react和redux连接的react-redux
。
阅读量:2036
点赞量:0
收藏量:0