Typescript - Redux-Thunk 流程知识点梳理

3,192 阅读4分钟
  • typescript redux thunk 流程梳理

    • 定义行为标识,此处 person 只有一个用于与服务器获取数据的行为

      • export enum personActionType {
          PERSON_QUERY_BASE = 'PERSON_QUERY_BASE'
        }
        
    • 根据行为标识派发 action

      // 接口 personGetInfoAction 定义了获取数据行为的 type 和 result
      /*
        export interface personGetInfoAction extends Action {
          type: personActionType.PERSON_QUERY_BASE
          result: PersonInfoResponse
        }
      */
      import { personActionType, personGetInfoAction } from '../type'
      import { Action } from 'redux'
      import { ThunkAction } from 'redux-thunk'
      import { queryInfo } from '../../api/person'
      import { Dispatch } from 'redux'
      
      export interface IPersonState {}
      
      type ThunkResult<R> = ThunkAction<
        R,
        IPersonState,
        undefined,
        Action<personGetInfoAction>
      >
      
      const queryBaseInfo = (): ThunkResult<void> => {
        return async (dispatch: Dispatch) => {
          const result = await queryInfo()
          dispatch({
            type: personActionType.PERSON_QUERY_BASE,
            result
          })
        }
      }
      
      • 在代码的开始,定义了一个空的接口 IPersonState 这样是为了能清楚的看懂 ThunkAction 接收的参数,如果有项目需要用到这个接口,可以方便的在里面添加数据类型

      • 接着定义了用于简化 ThunkAction 返回类型的 ThunkResult<R>

        • ThunkAction 泛型一共接收四个参数
          • R 函数返回类型,在函数queryBaseInfo中,不需要返回数据,即 R 为 void
          • IPersonState 可看上面的说的作用
          • undefined 这个参数用于接收额外的参数,通常以用于启用浏览器插件之类的事情,此处暂时没用,所以赋值 undefined 即可
          • Action<personGetInfoAction> 第四个参数是 action 表明了即将派发的action的类型定义,因为我知道接下来这个 action 是用于派发服务器返回数据的类型和结果,所以定义为 Action<personGetInfoAction>
      • 接着是具体派发行为的实现

        • 和同步的派发action不同,使用 redux-thunk 中间件派发异步action不是直接派发action对象,而是返回一个匿名函数,这个函数接收一个名为dispatch的参数,在匿名函数处理好异步逻辑后,由参数dispatch向 reducer 派发action对象
    • reducer 接收 actionCreator 派发过来的对象,进行状态的更新

      import { Reducer } from 'redux'
      import { personInfo, personGetInfoAction, personActionType } from '../type'
      
      export interface PersonState {
        baseInfo?: personInfo
      }
      
      let init_state: PersonState = {
        baseInfo: undefined
      }
      
      const person: Reducer<PersonState | undefined, personGetInfoAction> = (
        state = init_state,
        action: personGetInfoAction
      ) => {
        let newState = JSON.parse(JSON.stringify(state))
      
        switch (action.type) {
          case personActionType.PERSON_QUERY_BASE:
            let { code, data } = action.result
            if (code === 0) {
              newState.baseInfo = data
            }
            break
        }
        return newState
      }
      
      export default person
      
      • 前面的代码应该都能看懂,定义了state的类型以及初始化 state
      • 接着定义了 类型为Reducer名为person 的函数
        • 类型 Reducer接收两个泛型参数
          • 第一个是state,用于表明函数接收的state参数的类型以及最后返回state的类型
          • 第二个参数用于表明函数从actionCreator接收到的action
        • 在函数person中的业务逻辑就不详细说明了,都是一些简单处理 state之后返回新state的逻辑
    • configReducer.ts

      • import { combineReducers } from 'redux'
        import person, { PersonState } from './person'
        
        export interface AllState {
          person?: PersonState
        }
        
        let rootReducer = combineReducers<AllState, any>({
          person,
          course
        })
        
        export default rootReducer
        
      • 这个文件主要是用于把多个 reducer合并为一个,同时也可以进行一些其他的处理,暂时我还没用到其他的处理,以后用到再更新

    • store.js

      • import { createStore, applyMiddleware } from 'redux'
        import reduxThunk, { ThunkMiddleware } from 'redux-thunk'
        import rootReducer from './reducer/index'
        import { PersonState, personGetInfoAction } from './type'
        
        interface OtherState {}
        
        interface OtherAction {}
        
        type State = PersonState & OtherState
        type Actions = personGetInfoAction & OtherAction
        
        const store = createStore(
          rootReducer,
          applyMiddleware(reduxThunk as ThunkMiddleware<State, Actions>)
        )
        export default store
        
      • 用于生成最后项目中用到的总的 store ,代码应该不难看懂,不就详细解析了

    • Info.tsx

      import React, { MouseEvent } from 'react'
      /* 第三方模块 */
      import { withRouter, RouteComponentProps } from 'react-router-dom'
      import { connect } from 'react-redux'
      import { Button } from 'antd'
      
      /* 业务逻辑模块 */
      import action from '../../store/action'
      import { exitLogin } from '../../api/person'
      
      interface propFormDispatch {
        queryInfo: () => void
      }
      
      export type ApplicationProps = RouteComponentProps & propFormDispatch
      
      class Info extends React.Component<ApplicationProps, InfoState> {
        async componentDidMount() {
          await this.props.queryInfo()
        }
      
        public render() {
          return <div>// 业务逻辑</div>
        }
      }
      
      const mapState2Props = (state: InfoState) => {
        return {}
      }
      
      const mapDispatchToProps = {
        queryInfo: action.person.queryBaseInfo
      }
      
      export default withRouter(
        connect(
          mapState2Props,
          mapDispatchToProps
        )(Info)
      )
      
      • 这个文件其他的东西不就解析了

      • 主要用到了两个函数 mapStateToPropsmapDispatchToProps 通过 connect 函数把 redux 中获取的的 state挂载到 props

      • 因为使用到了react-router-dom进行组件的跳转,所以使用了RouteComponentPropshistory等属性挂载到 prop

      最后导出经过widthRouterconnect高阶后的组件

    • App.tsx

      import React from 'react'
      import { hot } from 'react-hot-loader'
      import { Provider } from 'react-redux'
      import store from './store/index'
      
      const App: React.FC = () => {
        return (
          <div className='App'>
            <Provider store={store}></Provider>
          </div>
        )
      }
      
      export default hot(module)(App)
      
      • 这里主要用到 Provider 把 store 挂载在 react实例上
      • 同时也因为使用了 react-hot-loader 实现开发的热加载,最后使用了高阶处理导出组件

文章就写到这里了,其实关于 redux 还有很多还没学懂的东西,也还有很多没用到,结合 typescript 使用还不熟练, 不过通过写这篇文章感觉自己对 redux-thunk 结合 typescript 理解得更清晰了。