在阅读此篇文章之前, 将默认你已经了解 Redux 的一些基础约束, 如: action,reducer 等, 对于在 TypeScript 的世界中使用 Redux , 对于其本身而言 (指 Reudx) 有很多不同的设计哲学, 看于使用者来如何设计它.
有些朋友可能喜欢写很原汁原味的 mapStateToProps 和 mapDispatchToProps, 如:
- export default connect(
- (state) => {
- return ...state
- },
- (dispatch) => {
- return {
- userLogin: (p) => {
- return dispatch(actions.userLogin(p))
- }
- }
- }
- )(Home)
那么有些朋友可能喜欢使用 bindActionCreators 精简代码, 如:
- import { bindActionCreators } from 'redux';
- export default connect(
- (state) => {
- return ...state
- },
- (dispatch) => {
- return {
- userLogin: bindActionCreators(actions.userLogin, dispatch)
- }
- }
- )(Home)
但我们明白从软件的设计的角度来说, 除了必要的性能优化外, 最主要的思考是着重于代码的阅读和可维护性, 因此也许我的方式并不是最好的, 但这是我个人使用起来最舒服的(优化是另外一个话题).
我期望使用一个单独的文件来创建 Store, 因为将来我们可以在这个单独的文件中去做一些优化, 并且将 reducer 分割成每一个 Page 级别页面自己的子 reducer, 如:
- import { createStore, applyMiddleware, combineReducers, compose } from "redux";
- import thunk from "redux-thunk";
- import { reduxReducers } from "@/pages/Redux/flow/reduxReducers";
- import { globalReducers } from "./reducers";
- const composeEnhancers = (Windows as any) && (Windows as any).REDUX_DEVTOOLS_EXTENSION_COMPOSE || compose;
- const reducer = combineReducers({
- global: globalReducers,
- reduxPage: reduxReducers,
- });
- export const configureStore = () => createStore(
- reducer,
- composeEnhancers(applyMiddleware(thunk)),
- );
我们用 combineReducers 来创建子 reducer, 并且使用了 thunk 中间件. 对于 Page 级别的 action,reducer 我习惯于将它们放置在具体组件的目录中, 并创建一个 flow 目录;
我们可以定义一些接口, 用于描述 action 即将返回的值, 我将它设计成这样的结构:
- {
- type: "",
- payload: {
- data: T,
- };
- }
因此, 它的接口是:
- export interface IAction<T =any> {
- type: string;
- payload: {
- data: T,
- };
- }
此外, 对于全局的 StoreState , 我们也定义一个接口, 用于描述 mapStateToProps 或, 如:
- export interface IStoreState {
- reduxPage: IReduxPageStoreState;
- global: IGlobalStoreState;
- }
接下来, 我们来写一个 reduxPage 的 reducer, 定义一个默认的 state , 并且个流程如下:
- import { IAction } from "@/global/types";
- import * as CONST from "./constants";
- import * as TYPES from "../types";
- const initState: TYPES.IReduxPageStoreState = {
- syncId: "默认值",
- asyncId: "默认值",
- };
- export function reduxReducers(state = initState, action: IAction): TYPES.IReduxPageStoreState {
- const { type, payload } = action;
- switch (type) {
- case CONST.SYNC_DATA:
- return { ...state, syncId: payload.data };
- case CONST.ASYNC_DATA:
- return { ...state, asyncId: payload.data };
- default:
- return { ...state };
- }
- }
然后我们写两个 Action , 一个是同步的, 一个是异步的, 如:
- import * as CONST from "./constants";
- import { Dispatch } from "redux";
- import { IAction } from "global/types";
- import { IDataAsync, IDataSync } from "./types";
- export const dataSync = (): IAction<IDataSync> => {
- const syncData = {
- type: CONST.SYNC_DATA,
- payload: {
- data: "https://GitHub.com/icepy",
- },
- };
- return syncData;
- };
- export const dataAsync = (parameter: string) => (dispatch: Dispatch<IAction<IDataAsync>>) => {
- const asyncData = {
- type: CONST.ASYNC_DATA,
- payload: {
- data: "https://icepy.me",
- },
- };
- setTimeout(() => {
- dispatch(asyncData);
- }, 2000);
- };
最后, 我们再定义一下 Props 的接口:
- import { IDataAsync, IDataSync } from "./flow/types";
- export interface IReduxPageActionsProps {
- dataSync: () => IAction<IDataSync>;
- dataAsync: (parameter: string) => (dispatch: Dispatch<IAction<IDataAsync>>) => void;
- }
整个代码如下:
- import * as React from "react";
- import { connect } from "react-redux";
- import * as actions from "./flow/actions";
- import {
- IReduxPageProps,
- IReduxPageState,
- } from "./types";
- import { IStoreState } from "../../global/types";
- import styles from "./style.CSS";
- import Button from "@material-ui/core/Button";
- import Tabs from "@material-ui/core/Tabs";
- import Tab from "@material-ui/core/Tab";
- import Typography from "@material-ui/core/Typography";
- const TabContainer: React.SFC = (props) => {
- return (
- <Typography component="div" style={{ padding: 8 * 3 }}>
- {props.children}
- </Typography>
- );
- };
- class ReduxComponent extends React.Component<IReduxPageProps, IReduxPageState> {
- constructor(props: IReduxPageProps) {
- super(props);
- this.state = {
- value: 0,
- };
- }
- public actionDataSync = () => {
- this.props.dataSync();
- }
- public actionDataAsync = () => {
- this.props.dataAsync("icepy");
- }
- public handleChange = (event: React.ChangeEvent<{}>, value: any) => {
- this.setState({ value });
- }
- public render() {
- const { reduxPage } = this.props;
- const { syncId, asyncId } = reduxPage;
- const { value } = this.state;
- return (
- <div className={styles["container"]}>
- <div>
- <Tabs value={value} onChange={this.handleChange}>
- <Tab label="同步 Action" />
- <Tab label="异步 Action" />
- </Tabs>
- </div>
- <div className={styles["contents"]}>
- {value === 0 && (
- <TabContainer>
- <Button variant="outlined" color="secondary" onClick={this.actionDataSync}>
操作
- </Button>
- <div className={styles["box"]}>
- syncId: { syncId }
- </div>
- </TabContainer>
- )}
- {value === 1 && (
- <TabContainer>
- <Button variant="outlined" color="primary" onClick={this.actionDataAsync}>
操作
- </Button>
- <div className={styles["box"]}>
- asyncId: { asyncId }
- </div>
- </TabContainer>
- )}
- </div>
- </div>
- );
- }
- }
- const mapStateToProps = (state: IStoreState) => {
- const { reduxPage, global } = state;
- return {
- reduxPage,
- global,
- };
- };
- const ReduxPage = connect(mapStateToProps, actions)(ReduxComponent);
- export default ReduxPage;
如果你想用 ThunkDispatch, 可以从 react-thunk 包里导出;
来源: https://juejin.im/entry/5bb0922a6fb9a05cf90843f3