每天对自己多问几个为什么,总是有着想象不到的收获。 一个菜鸟小白的成长之路(copyer)
dva 是一个基于 redux 和 redux-saga 的数据流方案
take: 监听action动作,会阻塞后面的代码执行。action被触发后,后面的代码才能执行。(阻塞函数) take(pattern)
put: 相当于redux的dispatch,用来触发action 的 effect,然后再reducer中接受。 (阻塞函数) put(action)
call: 调用其他的函数,相当于jS中的call方法,其实用法也是差不多的。注意: fn 函数可以是一个 Generator 函数,也可以是一个返回 Promise 的普通函数 (阻塞函数) call(fn, ...args)
fork: 跟call函数基本相似,都是调用其他函数(不是阻塞函数) fork(fn, args)
select: 获取整个store中的state,相当于 store.getState()
cancel: 一旦任务被 fork,可以使用 yield cancel(task) 来中止任务执行。(只是针对fork,没有阻塞性)
// take
// 监听type为ADD的action执行,直到这个action被触发了,才会执行下面的代码
yield take('ADD');
console.log(12323)
// put
// 触发reducer中的type为ADD,然后返回新的state
yield put({
type: 'ADD',
...payload
})
// call
export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
yield call(delay, 500)
//等待上面的delay函数执行完,才调用下面的代码
console.log(123)
// fork
export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
yield fork(delay, 500)
//不用等待上面的delay函数执行完,直接会调用下面的代码
console.log(123)
//selecct
let tempList = yield select(state => state.getTodoList.list) // state就是store的整体的state
用户需要改变数据的时候,需要通过dispatch发起一个action,同步操作会直接通过Reducers改变State;
如果是异步操作会先触发Effects然后流向Reducers最终改变State。
这里只是针对dva-core
// js
npm install dva-core
// ts
npm install dva-core-ts
// store.ts
import { create } from 'dva-core-ts'
import models from '../models'
const app = create()
// 依次注册nodel
models.forEach((model: any) => {
app.model(model)
})
// 启动引用,必须在注册之后,在获取store之前
app.start()
// 获取store
const store = app._store;
export default store
如果不想看类型分析,就直接看下面的代码使用(虽然是用ts写的,但是js的写法也是一样的)
export function create(hooksAndOpts?: DvaOption, createOpts?: CreateOpts): DvaInstance;
// 返回一个DvaInstance的类型
export interface DvaInstance {
use: (hooks: Hooks) => void; // 注册hook在应用上
model: (model: Model) => void; // 注册model
unmodel: (namespace: string) => void; // 卸载model
start: () => void; // 启动引用
_store: Store; // 拿去整个引用的store
}
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
接下来,就是主要定义model,
一个model都有完整的结构,state
、reducer
、effect
、namespace
effect
是用来处理异步的请求,然后把数据传递给reducer
,然后再修改state
(处理异步代码)
reducer
是用来修改state
的唯一渠道
state
就是用来保存数据的地方
namespace
用来标识唯一性
这个跟vuex
中的思想很相似。
export interface Model {
namespace: string;
state?: any;
reducers?: ReducersMapObject | ReducersMapObjectWithEnhancer;
effects?: EffectsMapObject;
subscriptions?: SubscriptionsMapObject;
}
使用
import { Model } from 'dva-core-ts'
state可以保存任何的数据,所以使用any (后面可以自定义类型)
export type Reducer<S = any, A extends Action = AnyAction> = (
state: S | undefined,
action: A
) => S
export type ReducersMapObject<S = any, A extends Action = Action> = {
[K in keyof S]: Reducer<S[K], A>
}
ReducersMapObject 是一个对象,对象的类型主要就是 Reducer类型,该接受一个泛型,传递的类型就是state的类型
使用
import { Reducer } from 'redux'
interface ICount {
count: number;
}
// 定义一个reducer函数
add: Reducer<ICount>
export interface EffectsMapObject {
[key: string]: Effect | EffectWithType;
}
export type Effect = (action: AnyAction, effects: EffectsCommandMap) => void;
export type EffectType = 'takeEvery' | 'takeLatest' | 'watcher' | 'throttle';
export type EffectWithType = [Effect, { type: EffectType }];
// 针对action
export interface Action<T = any> {
type: T
}
export interface AnyAction extends Action {
[extraProps: string]: any
}
// 总结: 简单来说,action中type是必填字段,其他的都是可key-value对应的
// 针对effects
export interface EffectsCommandMap {
put: typeof put;
call: typeof call;
select: typeof select;
take: typeof take;
cancel: typeof cancel;
[key: string]: any;
}
// 就是redux-saga中的几个函数
使用:
import { Effect } from 'dva-core-ts'
export interface SubscriptionsMapObject {
[key: string]: Subscription;
}
export type Subscription = (api: SubscriptionAPI, done: Function) => void;
export interface SubscriptionAPI {
history: History; // 用来监听路由跳转
dispatch: Dispatch<any>;
}
使用
只能dispatch所在model的reducer和effects
。返回一个函数
,该函数应该用来取消订阅
的该数据源。subscriptions: {
setup({ dispatch, history }) { // 路由变化,
console.log(history);
},
},
import { Model, Effect, SubscriptionsMapObject } from 'dva-core-ts'
import { Reducer } from 'redux'
// 定义state的类型
interface ICount {
count: number;
}
// 定义model的类型
export interface countModel extends Model {
namespace: 'count',
state: ICount,
effects: {
addAsync: Effect,
subAsync: Effect,
},
reducers: {
add: Reducer<ICount>,
sub: Reducer<ICount>
},
subscriptions: SubscriptionsMapObject
}
// 逻辑代码
export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const initState: ICount = {
count: 10
}
// 定义一个model,类型为countModel
const countModel: countModel = {
namespace: 'count',
state: initState,
reducers: {
add: (state, action) => {
return {...state, count: state?.count + action.payload.num}
},
sub: (state, action) => {
return {...state, count: state?.count as number - action.payload.num}
},
},
effects: {
*addAsync({payload}, {call ,put}) {
yield call(delay, 2000)
yield put({
type: 'add',
payload,
})
},
*subAsync({payload}, {call ,put}) {
yield call(delay, 2000)
yield put({
type: 'sub',
payload,
})
}
},
subscriptions: {
setup({ dispatch, history }) {
console.log(history);
},
},
}
export default countModel
// model.ts
import CountModel from './count'
export default [CountModel]
export {
connect,
connectAdvanced,
useSelector, // 拿去state
useDispatch, // dispatch,修改state的值
useStore,
DispatchProp,
shallowEqual,
} from 'react-redux';
import { useSelector, useDispatch } from 'dva-core-ts'
const CountCom = () => {
// 获取dispatch
const dispatch = useDispatch();
// 拿取值
const countReducer = useSelector((state: any) => {
return state.count
})
return (
<div>
<h3>count的值: {countReducer.count}</h3>
{/* 触发effects中的函数(异步修改)*/}
<Button onClick={() => dispatch({type: 'count/addAsync', payload: {num: 1}})}>加</Button>
{/* 触发reducer中的函数(同步修改)*/}
<Button onClick={() => dispatch({type: 'count/sub', payload: {num: 1}})}>减</Button>
</div>
)
}
dva-core的使用,跟vuex的用法还是很相似的,用来还是比较的简单。
主要就是要理解:
上面还有一个subscriptions
还没有使用,主要没有具体的使用,可能是在实际中没使用吧,不是很熟悉,记住有这一点就行了,偷笑!!!
上面的代码或者解释有错误的,欢迎指正。