likes
comments
collection
share

记录React-redux、redux-thunk使用

作者站长头像
站长
· 阅读数 49

前言

最近在用React-redux,记录一下使用过程,加深一下记忆。

正文

安装

yarn add redux react-redux redux-thunk

目录

记录React-redux、redux-thunk使用

我给自己项目建的目录是这样的store,这是没有分片情况下的,分片了可以在reducer里在划分仔细点,是用TS写,我就省略在js里的constants文件。没啥要求,个人喜欢,甚至把reducer写在views目录组件代码中,其实也是可以的,更好理解。

操作数据过程分两种,一种异步、一种同步,异步需要借助中间件实现,先说说同步操作下的情况

同步过程

数据流动大致过程:

  1. View页面,dispatch对应的action
  2. 到reducer对应type执行操作,修改state
  3. 最后在View层获取新的state

借个其他博客的图 记录React-redux、redux-thunk使用

代码:

store下的index.ts文件

代码里只有reducer来自外部,所以下一步就是搭建reducer

import { createStore } from 'redux'
import { reducer } from './reducer/index'

const store = createStore(reducer)

export default store

reducer下的index.ts文件

reducer是一个函数,函数参数一个是state,一个是action,函数返回一个对象,新的state。通过action中的type匹配switch里的执行操作(不一定要switch,if也行),switch里的case显示是字符串,state可以默认一个对象初始值,这里执行的操作是

SET_CARDINFO更新赋值state里cardInfo的值

CLEAR_CARDINFO更新清空state里cardInfo的值

import { CardInfo } from '@/views/register/type'
import * as Actions from '../actions'
import * as ActionTypes from '../actions/type'

export type CState = Readonly<typeof initState>

const initState = {
	cardInfo: {} as CardInfo,
}

export const reducer = (state = initState, action: Actions.All) => {
	// 有数据进行更新时,返回一个新的state
	switch (action.type) {
		case ActionTypes.SET_CARDINFO:
			return { cardInfo: { ...state.cardInfo, ...action.payload } }
		case ActionTypes.CLEAR_CARDINFO:
			return { cardInfo: {} }
		default:
			return state
	}
}

代码来源于外部的就是action文件夹,所以下一步是action

actions文件夹下的type.ts,这里把switch所需要匹配的type常量和类型写一起,所以不需要constant文件

// 声明常量类型actionType,并且直接声明常量
export const SET_CARDINFO = 'SET_CARDINFO'
export type SET_CARDINFO = typeof SET_CARDINFO

export const CLEAR_CARDINFO = 'CLEAR_CARDINFO'
export type CLEAR_CARDINFO = typeof CLEAR_CARDINFO

actions文件夹下的index.ts

import { CardInfo } from '@/views/register/type'
import * as ActionTypes from './type'
import { GetOrderRes } from '../../api/type'

interface SET_CARDINFO {
	type: ActionTypes.SET_CARDINFO
	payload: CardInfo
}
interface CLEAR_CARDINFO {
	type: ActionTypes.CLEAR_CARDINFO
	payload: unknown
}

export type All = SET_CARDINFO | CLEAR_CARDINFO
// 以上是为reducer里的action确认类型

// 以下是当我们在组件dispatch时,需要使用对应type
export function SET_CARDINFO(payload: Partial<GetOrderRes>): SET_CARDINFO {
	return {
        type: ActionTypes.SET_CARDINFO,
        payload,
	}
}
export function CLEAR_CARDINFO(): CLEAR_CARDINFO {
	return {
        type: ActionTypes.CLEAR_CARDINFO,
        payload: {},
	}
}

最后是到我们Views组件中调用dispatch派发action,并在组件中获取新的state

这里有两种方式,老的方式就是通过connect高阶组件包裹UI组件来实现调用dispatch,同步下的dispatch最终接受的实参是一个对象,Register就是我们的类组件或函数组件,通过props拿到需要执行的action,最终实现state的更新

import store from '@/store'
const Register: React.FC<any> = props => {
    ...业务代码
    //某个按钮点击清空cardInfo
    props.CLEAR_CARDINFO() 
    //通过getState()获取新的cardInfo
    console.log(store.getState().cardInfo)
}

const mapDispatchToProps = (dispatch: Dispatch<All>) => ({
	SET_CARDINFO: (payload: CardInfo) => dispatch(SET_CARDINFO(payload)),
	CLEAR_CARDINFO: () => dispatch(CLEAR_CARDINFO()),
})

const mapStateToProps = (state: CState) => ({
	cardInfo: state.cardInfo,
})

export default connect(mapStateToProps, mapDispatchToProps)(Register)

新的方式就是使用hook,redux有提供两个hook,我们不用再用connect组件,方便的很

    const Register: React.FC<any> = props => {
        ...业务代码
        const dispatch: Dispatch<All> = useDispatch()
        const state:CState = useSelector(state => state)
        //某个按钮点击清空cardInfo
        dispatch(CLEAR_CARDINFO())
        //通过useSelector)获取新的cardInfo
        console.log(state)
    }

异步过程

异步的过程就是中间加了个中间件,这是使用redux-thunk,middleware执行的是发起异步请求,等同于执行两次dispatch。

  1. View页面,dispatch对应的action
  2. middleware过程发起异步请求,返回结果再次dispatch,第二次dispatch其实是同步action
  3. 到reducer对应type执行操作,修改state
  4. 最后在View层获取新的state

记录React-redux、redux-thunk使用

代码

store下的index.ts文件

import { createStore, applyMiddleware, compose } from 'redux'
import { reducer } from './reducer/index'
import ThunkMiddleware from 'redux-thunk'

//这个是使用redux-devtools,可以一起配上
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose
const storeEnhancer = applyMiddleware(ThunkMiddleware)
const store = createStore(reducer, composeEnhancers(storeEnhancer))

export default store

redux下的index.ts不用修改

action下的index.ts文件

异步thunk,返回一个函数,参数接受异步请求的params,同步是返回一个对象,二次dispatch派发同步的action

export function SET_ASYNC_CARDINFO(query: GetOrderQuery) {
	return async (dispatch: Dispatch<any>) => {
		const { data } = await GetOrder(query)
		const info = data && useCardInfo(data) //处理返回的数据
		dispatch(SET_CARDINFO(info))
	}
}

最后到我们view组件里,实现state异步更新

    const Register: React.FC<any> = props => {
        ...业务代码
        const dispatch: Dispatch<All> = useDispatch()
        const state:CState = useSelector(state => state)
        //设置更新cardInfo
        dispatch(SET_ASYNC_CARDINFO({ orderNo: 111 }))
        //通过useSelector)获取新的cardInfo
        console.log(state)
    }

最后

这里有个不好的地方就是,如果我们只是希望接口中的某个数据实现数据共享,我们发起一个异步请求,获取到了想要的数据,存在在redux。然后我们自己本身的view页面也要进行接口的请求,设置state。这样就每次进来都发起了2次同样的请求,个人觉得不好,这是我使用下来留下的疑惑。

后面我在尝试一下其他的异步redux,还有promise、saga等等,看看做个对比啥的。

还有就是下次再说说分片和redux-toolkit

转载自:https://juejin.cn/post/7241118721171226683
评论
请登录