记录React-redux、redux-thunk使用
前言
最近在用React-redux,记录一下使用过程,加深一下记忆。
正文
安装
yarn add redux react-redux redux-thunk
目录
我给自己项目建的目录是这样的store,这是没有分片情况下的,分片了可以在reducer里在划分仔细点,是用TS写,我就省略在js里的constants文件。没啥要求,个人喜欢,甚至把reducer写在views目录组件代码中,其实也是可以的,更好理解。
操作数据过程分两种,一种异步、一种同步,异步需要借助中间件实现,先说说同步操作下的情况
同步过程
数据流动大致过程:
- View页面,dispatch对应的action
- 到reducer对应type执行操作,修改state
- 最后在View层获取新的state
借个其他博客的图
代码:
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。
- View页面,dispatch对应的action
- middleware过程发起异步请求,返回结果再次dispatch,第二次dispatch其实是同步action
- 到reducer对应type执行操作,修改state
- 最后在View层获取新的state
代码
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