1、Redux Toolkit
Redux Toolkit是官方推荐的编写Redux的逻辑写法 简称RTK, 这工具是为了实现标准化逻辑,方便对redux进行简化管理
2、Redux Toolkit的安装
npm install @reduxjs/toolkit
// 注意:这个工具简化的是操作,集成了redux-thunk以及redux-devtools的配置,但是react-redux这个依赖还是需要安装
redux toolkit核心api
confiqureStore: 包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux中间件,redux-thunk默认包含,并启用 Redux DevTools Extension
createSlice: 接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions.
createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成-个pending/fulfiled/rejected基于该承诺分派动作类型的 thunk
3、Redux Toolkit的使用
使用的例子是沿着一的例子进行调整
创建模块信息
import {
createSlice,
PayloadAction,
SliceCaseReducers,
} from '@reduxjs/toolkit';
export interface ICounterState {
counter: number;
userInfo: { name: string; email: string };
}
export interface ICounterReducer extends SliceCaseReducers<ICounterState> {
addNumberCounter: (
state: ICounterState,
action: PayloadAction<number>,
) => void;
reduceNumberCounter: (
state: ICounterState,
action: PayloadAction<number>,
) => void;
}
const counter = createSlice<ICounterState, ICounterReducer>({
name: 'counter',
initialState: {
counter: 0,
userInfo: {
name: '',
email: '',
},
},
reducers: {
addNumberCounter: (state: ICounterState, action: PayloadAction<number>) => {
state.counter += action.payload;
},
reduceNumberCounter: (
state: ICounterState,
action: PayloadAction<number>,
) => {
state.counter -= action.payload;
},
},
});
// 导出action
export const { addNumberCounter, reduceNumberCounter } = counter.actions;
// 导出reducer
export default counter.reducer;
store的总的出口
import { configureStore } from '@reduxjs/toolkit';
import counterReducer, { ICounterState } from './modules/counter';
export interface IRootState {
counter: ICounterState;
}
const store = configureStore({
reducer: { counter: counterReducer },
});
export default store;
组件根节点的导入
import { Provider } from 'react-redux';
import { PureComponent, ReactElement } from 'react';
import store from './store';
import Content from './components/content';
class App extends PureComponent {
public render(): ReactElement {
return (
<Provider store={store}>
<h1>this is app</h1>
<Content />
</Provider>
);
}
}
export default App;
组件中使用store
import { connect } from 'react-redux';
import { PureComponent, ReactElement } from 'react';
import {
addNumberCounter,
reduceNumberCounter,
} from '../store/modules/counter';
import { IRootState } from '../store';
import { Dispatch } from '@reduxjs/toolkit';
interface IContentProps {
counter: number;
userInfo: { name: string; email: string };
addCounter: (num: number) => void;
reduceCounter: (num: number) => void;
}
class Content extends PureComponent<IContentProps> {
public render(): ReactElement {
const { counter, addCounter, reduceCounter, userInfo } = this.props;
return (
<div>
<div>
<span>counter: </span>
<span>{counter}</span>
</div>
<div>
<button onClick={() => addCounter(1)}>+1</button>
<button onClick={() => reduceCounter(1)}>-1</button>
</div>
<div>
<div>
<span>user: </span>
<span>{userInfo.name}</span>
</div>
<div>
<span>email: </span>
<span>{userInfo.email}</span>
</div>
<button>获取用户信息</button>
</div>
</div>
);
}
}
// 这个是把state注入到props
const mapStateToProps = (state: IRootState) => {
return {
counter: state.counter.counter,
userInfo: state.counter.userInfo,
};
};
// 这个是把方法注入的props
const mapDispatchToProps = (dispatch: Dispatch) => ({
addCounter: (num: number) => dispatch(addNumberCounter(num)),
reduceCounter: (num: number) => dispatch(reduceNumberCounter(num)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Content);
4、Redux Toolkit中进行接口或者异步操作
添加异步函数后的模块
import {
createAsyncThunk,
createSlice,
PayloadAction,
SliceCaseReducers,
SerializedError,
} from '@reduxjs/toolkit';
const userInfoApi = (): Promise<{ name: string; email: string }> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ name: 'even', email: '*****@163.com' });
// reject('this is error');
}, 1000);
});
};
export const fetchUserInfo = createAsyncThunk(
'fetchUserInfo',
() => userInfoApi(), // 如果是通过axios时,那么需要返回data
);
export interface ICounterState {
counter: number;
userInfo: { name: string; email: string };
}
export interface ICounterReducer extends SliceCaseReducers<ICounterState> {
addNumberCounter: (
state: ICounterState,
action: PayloadAction<number>,
) => void;
reduceNumberCounter: (
state: ICounterState,
action: PayloadAction<number>,
) => void;
}
const counter = createSlice<ICounterState, ICounterReducer>({
name: 'counter',
initialState: {
counter: 0,
userInfo: {
name: '',
email: '',
},
},
reducers: {
addNumberCounter: (state: ICounterState, action: PayloadAction<number>) => {
state.counter += action.payload;
},
reduceNumberCounter: (
state: ICounterState,
action: PayloadAction<number>,
) => {
state.counter -= action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchUserInfo.pending, (state: ICounterState) => {
console.log(state);
//写加载的逻辑
})
.addCase(
fetchUserInfo.fulfilled,
(
state: ICounterState,
action: PayloadAction<{ name: string; email: string }>,
) => {
console.log('fulfilled', state, action);
// 写成功的逻辑
state.userInfo.name = action.payload.name;
state.userInfo.email = action.payload.email;
},
)
.addCase(
fetchUserInfo.rejected,
(
state: ICounterState,
action: PayloadAction<unknown, string, unknown, SerializedError>,
) => {
console.log('reject', state, action.error);
// 写错误的逻辑
},
);
},
});
// 导出action
export const { addNumberCounter, reduceNumberCounter } = counter.actions;
// 导出reducer
export default counter.reducer;
在出口处导出类型
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './modules/counter';
const store = configureStore({
reducer: { counter: counterReducer },
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch<any>;
export default store;
这样就可以在组件中进行正常使用了
import { connect } from 'react-redux';
import { PureComponent, ReactElement } from 'react';
import {
addNumberCounter,
reduceNumberCounter,
fetchUserInfo,
} from '../store/modules/counter';
import { AppDispatch, RootState } from '../store';
interface IContentProps {
counter: number;
userInfo: { name: string; email: string };
addCounter: (num: number) => void;
reduceCounter: (num: number) => void;
fetchUserInfo: () => Promise<void>;
}
class Content extends PureComponent<IContentProps> {
public render(): ReactElement {
const { counter, addCounter, reduceCounter, userInfo, fetchUserInfo } =
this.props;
return (
<div>
<div>
<span>counter: </span>
<span>{counter}</span>
</div>
<div>
<button onClick={() => addCounter(1)}>+1</button>
<button onClick={() => reduceCounter(1)}>-1</button>
</div>
<div>
<div>
<span>user: </span>
<span>{userInfo.name}</span>
</div>
<div>
<span>email: </span>
<span>{userInfo.email}</span>
</div>
<button onClick={() => fetchUserInfo()}>获取用户信息</button>
</div>
</div>
);
}
}
// 这个是把state注入到props
const mapStateToProps = (state: RootState) => {
return {
counter: state.counter.counter,
userInfo: state.counter.userInfo,
};
};
// 这个是把方法注入的props
const mapDispatchToProps = (dispatch: AppDispatch) => ({
addCounter: (num: number) => dispatch(addNumberCounter(num)),
reduceCounter: (num: number) => dispatch(reduceNumberCounter(num)),
fetchUserInfo: () => dispatch(fetchUserInfo()),
});
export default connect(mapStateToProps, mapDispatchToProps)(Content);
使用hook进行react-redux的调用,替代connect
import { FC, ReactElement } from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import { RootState } from '../store';
import {
addNumberCounter,
reduceNumberCounter,
} from '../store/modules/counter';
const Content: FC = (): ReactElement => {
const { counter } = useSelector(
(state: RootState) => ({
counter: state.counter.counter,
}), // 这个是把需要用的redux数据映射到返回的json上,方便调用,即解构出来的counter值
shallowEqual, // 这个参数是进行浅层比较,为了提高性能
);
const dispatch = useDispatch();
return (
<div>
<h2>counter: {counter}</h2>
<button onClick={() => dispatch(addNumberCounter(1))}>+1</button>
<button onClick={() => dispatch(reduceNumberCounter(1))}>-1</button>
</div>
);
};
export default Content;
5、redux中间件初探
实现一个日志打印的中间件
import { AnyAction, configureStore, Dispatch } from '@reduxjs/toolkit';
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import counterReducer from './modules/counter';
const store = configureStore({
reducer: { counter: counterReducer },
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch<any>;
const log = (store: ToolkitStore) => {
const next: Dispatch<AnyAction> = store.dispatch;
const wrapDispatch = <T extends AnyAction>(action: T): T => {
console.log('this is before dispatch');
next(action);
console.log(store.getState());
console.log('this is after dispatch');
return action;
};
store.dispatch = wrapDispatch;
};
log(store);
export default store;
手动实现一个thunk
const thunk = (store: ToolkitStore) => {
const next = store.dispatch;
const dispatchThunk = <T extends AnyAction | Function>(action: T): T => {
if (typeof action === 'function') {
action(store.dispatch, store.getState);
} else {
next(action);
}
return action;
};
store.dispatch = dispatchThunk;
};
thunk(store)
实现一个applyMiddleWare