react项目使用redux入门-6-redux-thunk中间件处理异步请求(The react project uses the Redux starter-6-redux thunk middleware to handle asynchronous requests)

场景:切换语言、请求产品列表

  • src/index.tsx
    import React from ‘react’
    import ReactDOM from ‘react-dom’
    import { Provider } from ‘react-redux’ // react-redux 利用上下文context,提供的数据组件Provider
    import ‘antd/dist/antd.css’
    import ‘./index.css’
    import App from ‘./App’
    import ‘i18n’

    import store from ‘redux/store’

    ReactDOM.render(

    {/* 使用Provider, 并加载 数据仓库 store, 就可以在全局范围内使用store */}



    ,
    document.getElementById(‘root’)
    )

  • ListPage组件中请求数据
    import { Component } from ‘react’
    import { connect } from ‘react-redux’
    import { ThunkDispatch } from ‘redux-thunk’ // ThunkDispatch 为了支持函数类型的action,提供dispatch的类型
    import { RootState } from ‘redux/store’
    import { ActionsProductProps, fetchProductsActionCreator } from ‘redux/product/actions’

    const mapStateToProps = ({ product }) => product
    // const mapStateToProps = ({ product }: RootState) => {
    // return {
    // loading: product.loading,
    // productList: product.productList
    // }
    // }

    const mapDispatchToProps = (dispatch: ThunkDispatch ) => {
    fetchProduct : () => dispatch(fetchProductsActionCreator())
    }

    class ListPageComponent extends Component & ReturnType, any> {

    componentDidMount() {
    const { fetchProduct } = this.props
    fetchProduct()
    }

    render(){
    const { productList, loading } = this.props
    return loading?

    正在加载……

    🙁

    {
    productList.map(item =>

    {item.name}

    )
    }

    )
    }
    }
    export const ListPage = connect(mapStateToProps, mapDispatchToProps)(ListPageComponent)

  • Header组件中使用
    import { Component } from ‘react’
    import { withRouter, RouteComponentProps } from ‘react-router-dom’
    import { MenuInfo } from ‘rc-menu/lib/interface’
    import { nanoid } from ‘nanoid’
    // 使用react-redux
    import { Dispatch } from ‘redux’
    import { connect } from ‘react-redux’
    import store, { RootState } from ‘redux/store’
    import { addLanguageActionCreator, changeLanguageActionCreator } from ‘redux/language/actionCreators’

    const mapStateToProps = (state: RootState) => {
    return {
    lng: state.language.lng,
    languageList: state.language.languageList
    }
    }

    const mapDispatchToProps = (dispatch: Dispatch) => {
    return {
    addLanguageDispatch: (language: { code: string, language: string }) => {
    dispatch(addLanguageActionCreator(language))
    }
    changeLanguageDispatch: (lng: ‘en’|’zh’) => {
    dispatch(changeLanguageActionCreator(lng))
    }
    }
    }

    class HeaderComponent extends Component & ReturnType>{
    render(){
    const { history, lng, languageList, addLanguageDispatch, changeLanguageDispatch } = this.props

    /* meun 的点击事件 */
    const oprateLanguage = ( e: MenuInfo ) => {
    if(e.key !== ‘new’){
    changeLanguageDispatch(e.key)
    }else{
    addLanguageDispatch({code: `lng${nanoid()}`, language: ‘新语种’})
    }
    }

    const menu = (



    添加新语言

    {languageList.map(language => (

    {language.language}

    ))}

    )

    return (

    让旅游更幸福


    )
    }
    }

    export const Header = connect(mapStateToProps, mapDispatchToProps)(withRouter(HeaderComponent))

  • store数据封装

    新建目录:src/redux、src/redux/language
    新建文件:src/redux/store.ts、src/redux/language/actionCreators.ts,src/redux/language/reducers.ts

    mkdir src/redux src/redux/language
    touch src/redux/language/actionCreators.ts src/redux/language/reducer.ts
    touch src/redux/product/actions.ts src/redux/product/reducer.ts

    store.ts

    import { createStore, combineReducers, applyMiddleware } from ‘redux’
    import languageReducer form ‘./language/reducer.ts’
    import productReducer form ‘./product/reducer.ts’
    import thunk form ‘redux-thunk’

    // 使用 combineReducers方法,连接多个reducer
    const rootReducer = combineReducers({
    language: languageReducer,
    product: productReducer
    })

    // redux-thunk 使用thunk中间件,使得redux的action除了对象形式,还是支持函数类型
    const store = createStore(rootReducer, applyMiddleware(thunk))

    // 使用ts的条件类型 ReturnType,T:函数类型。 获取函数返回值的类型
    export type RootState = ReturnType

    export default store

    product:- product/actions.ts、product/reducers.ts

    actions.ts

    // 使用redux-thunk,支持函数类型的action,这个action的类型:ThunkAction
    import { ThunkAction } from ‘react-thunk’
    import axios from ‘axios’
    import { RootState } from ‘redux/store’

    // fetch_start 主要是用来控制loading, 开始发接口,loading值为true
    export const FETCH_START = ‘product/fetch_start’
    // fetch_success 主要用来 接口数据成功,将数据data存入store
    export const FETCH_SUCCESS = ‘product/fetch_success’
    export const FETCH_FAIL = ‘product/fetch_fail’

    interface ActionFetchStartProps { type: typeof FETCH_START}
    interface ActionFetchSuccessProps { type: typeof FETCH_SUCCESS, payload: any}
    interface ActionFetchFailProps { type: typeof FETCH_FAIL }
    export type ActionsProductProps = ActionFetchStartProps | ActionFetchSuccessProps | ActionFetchFailProps

    // actionCreator
    export const fetchStartActionCreator = (): ActionFetchStartProps => ({ type: FETCH_START})
    export const fetchSuccessActionCreator = (data:any):ActionFetchSuccessProps => ({ type: FETCH_SUCCESS, payload: data })
    export const fetchFailActionCreator = ():ActionFetchFailProps => ({ type: FETCH_FAIL })

    // 函数类型的action
    export const fetchProductsActionCreator = (): ThunkAction=> async (dispatch, getState) => {
    dispactch(fetchStartActionCreator())
    try{
    const { data } = await axios.get(‘/api/products’)
    dispatch(fetchSuccessActionCreator(data))
    }catch{
    dispatch(fetchFailActionCreator())
    }
    }

    reducer.ts

    import { ActionsProductProps, FETCH_START, FETCH_SUCCESS, FETCH_FAIL } from ‘./actions.ts’

    interface ProductState {
    loading: boolean,
    productList: any[]
    }
    const defaultState: ProductState = {
    loading: false,
    productList: []
    }
    export default (state = defaultState, action:ActionsProductProps ) => {
    switch (action.type) {
    case FETCH_START:
    return { …state, loading: true }
    case FETCH_SUCCESS:
    return { …state, loading: false, tourists: action.payload }
    case FETCH_FAIL:
    return { …state, loading: false }
    default:
    return state
    }
    }

    language:- language/actions.ts、language/reducers.ts

    actions.ts

    /* 用常量定义action.type,减少代码敲错 */
    export const ADD_LANGUAGE = ‘language/add’
    export const CHANGE_LANGUAGE = ‘language/change’

    /* action的类型申明 */
    const AddActionProps = {
    type: typeof ADD_LANGUAGE,
    payload: { code: string, language: string }
    }
    const ChangeActionProps = {
    type: typeof CHANGE_LANGUAGE,
    payload: ‘zh’ | ‘en’
    }

    export type LanguageActionProps = AddActionProps | ChangeActionProps

    /* 用工厂模式创建action */
    export const addLanguageActionCreator = (language: {code: string, language: string}):ADD_LANGUAGE => {
    return {
    type: ADD_LANGUAGE,
    payload: language
    }
    }
    export const changeLanguageActionCreator = (lng: ‘zh’ | ‘en’):CHANGE_LANGUAGE => {
    return {
    type: CHANGE_LANGUAGE,
    payload: lng
    }
    }

    2. `reducer.ts`

    import { ADD_LANGUAGE, CHANGE_LANGUAGE, LanguageActionProps } from ‘./actions’

    export interface LanguageState {
    lng: ‘zh’ | ‘en’,
    languageList: {code: string, language: string}[]
    }

    const defaultStoreState: LanguageState = {
    lng: ‘zh’,
    languageList: [{ code: ‘zh’, language: ‘中文’}, { code: ‘en’, language: ‘English’}]
    }

    export default (state = defaultStoreState, action:LanguageActionProps) => {
    switch (action.type) {
    case CHANGE_LANGUAGE:
    return { …state, lng: action.payload }
    case ADD_LANGUAGE:
    return { …state, languageList: […state.languageList, action.payload] }
    default:
    return state
    }
    }

  • 新建目录:src/redux、src/redux/language
  • 新建文件:src/redux/store.ts、src/redux/language/actionCreators.ts,src/redux/language/reducers.ts
  • store.ts
  • product:- product/actions.ts、product/reducers.ts
  • actions.ts
  • reducer.ts
  • language:- language/actions.ts、language/reducers.ts
  • actions.ts
————————

Scenario: switching languages and requesting product lists

  • src/index.tsx
    import React from ‘react’
    import ReactDOM from ‘react-dom’
    import { Provider } from ‘react-redux’ // react-redux 利用上下文context,提供的数据组件Provider
    import ‘antd/dist/antd.css’
    import ‘./index.css’
    import App from ‘./App’
    import ‘i18n’

    import store from ‘redux/store’

    ReactDOM.render(

    {/* 使用Provider, 并加载 数据仓库 store, 就可以在全局范围内使用store */}



    ,
    document.getElementById(‘root’)
    )

  • ListPage组件中请求数据
    import { Component } from ‘react’
    import { connect } from ‘react-redux’
    import { ThunkDispatch } from ‘redux-thunk’ // ThunkDispatch 为了支持函数类型的action,提供dispatch的类型
    import { RootState } from ‘redux/store’
    import { ActionsProductProps, fetchProductsActionCreator } from ‘redux/product/actions’

    const mapStateToProps = ({ product }) => product
    // const mapStateToProps = ({ product }: RootState) => {
    // return {
    // loading: product.loading,
    // productList: product.productList
    // }
    // }

    const mapDispatchToProps = (dispatch: ThunkDispatch ) => {
    fetchProduct : () => dispatch(fetchProductsActionCreator())
    }

    class ListPageComponent extends Component & ReturnType, any> {

    componentDidMount() {
    const { fetchProduct } = this.props
    fetchProduct()
    }

    render(){
    const { productList, loading } = this.props
    return loading?

    正在加载……

    🙁

    {
    productList.map(item =>

    {item.name}

    )
    }

    )
    }
    }
    export const ListPage = connect(mapStateToProps, mapDispatchToProps)(ListPageComponent)

  • Header组件中使用
    import { Component } from ‘react’
    import { withRouter, RouteComponentProps } from ‘react-router-dom’
    import { MenuInfo } from ‘rc-menu/lib/interface’
    import { nanoid } from ‘nanoid’
    // 使用react-redux
    import { Dispatch } from ‘redux’
    import { connect } from ‘react-redux’
    import store, { RootState } from ‘redux/store’
    import { addLanguageActionCreator, changeLanguageActionCreator } from ‘redux/language/actionCreators’

    const mapStateToProps = (state: RootState) => {
    return {
    lng: state.language.lng,
    languageList: state.language.languageList
    }
    }

    const mapDispatchToProps = (dispatch: Dispatch) => {
    return {
    addLanguageDispatch: (language: { code: string, language: string }) => {
    dispatch(addLanguageActionCreator(language))
    }
    changeLanguageDispatch: (lng: ‘en’|’zh’) => {
    dispatch(changeLanguageActionCreator(lng))
    }
    }
    }

    class HeaderComponent extends Component & ReturnType>{
    render(){
    const { history, lng, languageList, addLanguageDispatch, changeLanguageDispatch } = this.props

    /* meun 的点击事件 */
    const oprateLanguage = ( e: MenuInfo ) => {
    if(e.key !== ‘new’){
    changeLanguageDispatch(e.key)
    }else{
    addLanguageDispatch({code: `lng${nanoid()}`, language: ‘新语种’})
    }
    }

    const menu = (



    添加新语言

    {languageList.map(language => (

    {language.language}

    ))}

    )

    return (

    让旅游更幸福


    )
    }
    }

    export const Header = connect(mapStateToProps, mapDispatchToProps)(withRouter(HeaderComponent))

  • store数据封装

    新建目录:src/redux、src/redux/language
    新建文件:src/redux/store.ts、src/redux/language/actionCreators.ts,src/redux/language/reducers.ts

    mkdir src/redux src/redux/language
    touch src/redux/language/actionCreators.ts src/redux/language/reducer.ts
    touch src/redux/product/actions.ts src/redux/product/reducer.ts

    store.ts

    import { createStore, combineReducers, applyMiddleware } from ‘redux’
    import languageReducer form ‘./language/reducer.ts’
    import productReducer form ‘./product/reducer.ts’
    import thunk form ‘redux-thunk’

    // 使用 combineReducers方法,连接多个reducer
    const rootReducer = combineReducers({
    language: languageReducer,
    product: productReducer
    })

    // redux-thunk 使用thunk中间件,使得redux的action除了对象形式,还是支持函数类型
    const store = createStore(rootReducer, applyMiddleware(thunk))

    // 使用ts的条件类型 ReturnType,T:函数类型。 获取函数返回值的类型
    export type RootState = ReturnType

    export default store

    product:- product/actions.ts、product/reducers.ts

    actions.ts

    // 使用redux-thunk,支持函数类型的action,这个action的类型:ThunkAction
    import { ThunkAction } from ‘react-thunk’
    import axios from ‘axios’
    import { RootState } from ‘redux/store’

    // fetch_start 主要是用来控制loading, 开始发接口,loading值为true
    export const FETCH_START = ‘product/fetch_start’
    // fetch_success 主要用来 接口数据成功,将数据data存入store
    export const FETCH_SUCCESS = ‘product/fetch_success’
    export const FETCH_FAIL = ‘product/fetch_fail’

    interface ActionFetchStartProps { type: typeof FETCH_START}
    interface ActionFetchSuccessProps { type: typeof FETCH_SUCCESS, payload: any}
    interface ActionFetchFailProps { type: typeof FETCH_FAIL }
    export type ActionsProductProps = ActionFetchStartProps | ActionFetchSuccessProps | ActionFetchFailProps

    // actionCreator
    export const fetchStartActionCreator = (): ActionFetchStartProps => ({ type: FETCH_START})
    export const fetchSuccessActionCreator = (data:any):ActionFetchSuccessProps => ({ type: FETCH_SUCCESS, payload: data })
    export const fetchFailActionCreator = ():ActionFetchFailProps => ({ type: FETCH_FAIL })

    // 函数类型的action
    export const fetchProductsActionCreator = (): ThunkAction=> async (dispatch, getState) => {
    dispactch(fetchStartActionCreator())
    try{
    const { data } = await axios.get(‘/api/products’)
    dispatch(fetchSuccessActionCreator(data))
    }catch{
    dispatch(fetchFailActionCreator())
    }
    }

    reducer.ts

    import { ActionsProductProps, FETCH_START, FETCH_SUCCESS, FETCH_FAIL } from ‘./actions.ts’

    interface ProductState {
    loading: boolean,
    productList: any[]
    }
    const defaultState: ProductState = {
    loading: false,
    productList: []
    }
    export default (state = defaultState, action:ActionsProductProps ) => {
    switch (action.type) {
    case FETCH_START:
    return { …state, loading: true }
    case FETCH_SUCCESS:
    return { …state, loading: false, tourists: action.payload }
    case FETCH_FAIL:
    return { …state, loading: false }
    default:
    return state
    }
    }

    language:- language/actions.ts、language/reducers.ts

    actions.ts

    /* 用常量定义action.type,减少代码敲错 */
    export const ADD_LANGUAGE = ‘language/add’
    export const CHANGE_LANGUAGE = ‘language/change’

    /* action的类型申明 */
    const AddActionProps = {
    type: typeof ADD_LANGUAGE,
    payload: { code: string, language: string }
    }
    const ChangeActionProps = {
    type: typeof CHANGE_LANGUAGE,
    payload: ‘zh’ | ‘en’
    }

    export type LanguageActionProps = AddActionProps | ChangeActionProps

    /* 用工厂模式创建action */
    export const addLanguageActionCreator = (language: {code: string, language: string}):ADD_LANGUAGE => {
    return {
    type: ADD_LANGUAGE,
    payload: language
    }
    }
    export const changeLanguageActionCreator = (lng: ‘zh’ | ‘en’):CHANGE_LANGUAGE => {
    return {
    type: CHANGE_LANGUAGE,
    payload: lng
    }
    }

    2. `reducer.ts`

    import { ADD_LANGUAGE, CHANGE_LANGUAGE, LanguageActionProps } from ‘./actions’

    export interface LanguageState {
    lng: ‘zh’ | ‘en’,
    languageList: {code: string, language: string}[]
    }

    const defaultStoreState: LanguageState = {
    lng: ‘zh’,
    languageList: [{ code: ‘zh’, language: ‘中文’}, { code: ‘en’, language: ‘English’}]
    }

    export default (state = defaultStoreState, action:LanguageActionProps) => {
    switch (action.type) {
    case CHANGE_LANGUAGE:
    return { …state, lng: action.payload }
    case ADD_LANGUAGE:
    return { …state, languageList: […state.languageList, action.payload] }
    default:
    return state
    }
    }

  • 新建目录:src/redux、src/redux/language
  • 新建文件:src/redux/store.ts、src/redux/language/actionCreators.ts,src/redux/language/reducers.ts
  • store.ts
  • product:- product/actions.ts、product/reducers.ts
  • actions.ts
  • reducer.ts
  • language:- language/actions.ts、language/reducers.ts
  • actions.ts