简介
-
dva
是一个基于redux
和redux-saga
的数据流方案 -
dva
=React-Router
(路由) +Redux
(架构) +Redux-saga
(异步操作) -
dva
通过model
的概念把一个领域的模型管理起来,包含同步更新state
的reducers
,处理异步逻辑的effects
,订阅数据源的subscriptions
数据流向
-
数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的
-
当此类行为会改变数据的时候可以通过
dispatch
发起一个action
-
如果是同步行为会直接通过
Reducers
改变State
-
如果是异步行为(副作用)会先触发
Effects
然后流向Reducers
最终改变State
Models
-
dva
通过一个个的model
来管理数据。这些处理数据流的文件统一放在models
文件夹下,每一个文件默认导出一个对象,里面包含数据和处理数据的方法 -
model
文件的基本结构
export default {
// 全局 state 上的 key,必须全局唯一
namespace: 'example',
// 初始数据
state: {
count: 0,
},
// 管理同步方法,必须是纯函数
reducers: {
save() { ... },
},
// 管理异步操作,采用了 generator 的相关概念
effects: {
*getData() { ... },
},
// 订阅数据源
subscriptions: {
setup() { ... },
}
}
State
-
State
表示Model
的状态数据 -
操作的时候保证每次都是全新对象
Reducer
-
Reducer
(也称为reducing function
)函数用于改变State
的值。就是描述如何改变数据的方法 -
Reducer
必须是纯函数。接受state
和action
作为参数,即:(state, action) => state
。在函数中更改旧的state
,返回新的state
export default {
namespace: 'demo',
// ...
state: {
// ...
name: ''
},
reducers: {
/**
* state:旧的state
* payload:传过来的数据。第二个参数是action,这个action带有传过来的payload
*/
save(state, { payload: name }) {
// return 新的state,这样页面就会更新
// es6语法,就是把state全部展开,通过后面的payload去覆盖前面的
return ({ ...state, name })
}
}
}
- 通过
dispatch
函数触发一个action
,来触发Reducer
this.props.dispatch({
type: 'demo/save',
payload: name
})
Effect
-
Effect
被称为副作用。可以在这里获取你需要的数据,例如向服务器发起一个请求、或是获取其他model
里的state
-
常用来处理异步操作等,基于
Redux-saga
实现 -
采用了
generator
的相关概念,内部使用yield
关键字,标识每一步的操作 -
每一个
effect
都可以接收两个参数:包含dispatch
携带参数payload
的action
对象、dva
提供的effect
函数内部的处理函数集
提供的处理函数:
call
:执行异步函数。可以理解为等待这个函数执行结束。const data = yield call(doSomethingFunc, parameter);
put
:发出一个Action
,类似于dispatch
。这个action
既可以是一个reducer
也可以是一个effect
。yield put({ type: 'reducerName', payload: { page } });
select
:用于获取当前或其他model
的state
。const data = yield select(states => states[namespace]);
import * as services from '@/services';
export default {
namespace: 'pageModel',
state: {
title: 'Welcome to Wise.Wrong\'s Bolg',
name: 'wise'
},
effects: {
/**
* payload是传来的参数,如果没参数可以写成(_, { call,put,select })
*/
*testEffect({ payload }, { call, put, select }) {
// 获取state中的值 pageModel就是对应的namespace(唯一)
const { name } = yield select(state => state.pageModel);
// 接口入参
const params = { name, ...payload };
// services.getInfo是封装好的请求,params是要给这个请求传入的参数
const { data } = yield call(services.getInfo, params);
// 请求成功之后,调用reducer同步方法更新state
yield put({
// 调用当前 model 的 action 不需要添加 namespace
type: 'changeTitle',
payload: data,
});
}
},
reducers: {
changeTitle(state, { payload }) {
return {
...state,
title: payload
};
},
},
};
Subscription
-
Subscription
语义是订阅,用于订阅一个数据源,然后根据条件dispatch
需要的action
-
数据源可以是当前的时间、服务器的
websocket
连接、keyboard
输入、geolocation
变化、history
路由变化等 -
项目中常用于页面初始化数据的自动请求
export default {
// ...
subscriptions: {
// 订阅监听,比如我们监听路由,进入页面就如何,可以在这写
setup({ dispatch, history }) {
return history.listen(({ pathname, query }) => {
// 进入 '/home' 路由,发起一个名叫 'query' 的 effect
if (pathname === '/home') {
dispatch({ type: 'query' });
}
});
},
},
}
connect
connect
的作用是将model
和component
串联起来,绑定State
到View
@connect
函数接收两个参数:- 第一个参数接收一个函数,这个函数的参数为
state
,state
是一个全局state
变量,需要借助命名空间来指定对应的具体module
,这个函数的作用是返回module
中的数据,并且绑定到this.props
上面,所以最后记得要return
- 第二个参数也是一个函数,接收
dispatch
参数,此参数作用是调用model
层定义的函数,有返回值,返回值是一个对象,其中的函数可以被this.props
调用
// 写法一 - mapStateToProps / mapDispatchToProps
import { connect } from 'dva';
function App({ dispatch, user }) {
const handleClick = () => {
dispatch({ type: 'user/fetchUser' })
}
return (
<div>
<h2>Hello, {user}</h2>
<button onClick={handleClick}>Click me</button>
</div>
)
}
function mapStateToProps(state) {
return { user: state.user };
// 返回之后就可以直接这么用 this.props.user
}
function mapDispatchToProps(dispatch) {
return {
fetchUser: () => {
dispatch({ type: 'user/fetchUser' })
}
}
// 返回之后就可以直接用 this.props.fetchUser
// 如果没有这个方法 就需要这么调用 this.props.dispatch({ type: 'user/fetchUser' })
}
// export default connect(mapStateToProps, mapDispatchToProps)(App);
export default connect(mapStateToProps)(App);
// 写法二 - 匿名函数
import { connect } from 'dva';
function App({ dispatch, user }) {
const handleClick = () => {
dispatch({ type: 'user/fetchUser' })
}
return (
<div>
<h2>Hello, {user}</h2>
<button onClick={handleClick}>Click me</button>
</div>
)
}
export default connect(({ user }) => ({ user }))(App);
// 写法三 - ES7修饰器写法
import { connect } from 'dva';
@connect(({ user }) => ({ user }))
function App({ dispatch, user }) {
const handleClick = () => {
dispatch({ type: 'user/fetchUser' })
}
return (
<div>
<h2>Hello, {user}</h2>
<button onClick={handleClick}>Click me</button>
</div>
)
}
export default App;
发表评论