namespace
namespace为model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持通过 .
的方式创建多层命名空间
// model/test.js
const TestModel = {
namespace: "test"
};
export default TestModel
// pages/test/test.js
import { connect } from 'dva';
export default
@connect()
class Test extends React.Component {
componentDidMount() {
const { dispatch } = this.props
dispatch({
type: 'test/**'
}
/**
* 这里的type: 'test/**' test就是我们在namespace中设置的model命名
* test/** "/"后为effects中的方法
*/
}
}
state
state属性 model中用于数据存储
// model/test.js
const TestModel = {
namespace: "test",
state: {
data: [1, 2, 3],
str: 'model-state'
}
};
export default TestModel
// pages/test/test.js
import { connect } from 'dva';
/**
* connect(({ test }) => ({}))
* 通过connect建立namespace名为test的model与当前组件的链接
* 并读取model中的state与当前组件的props进行绑定
*/
export default
@connect(({ test }) => ({
data: test.data,
str: test.str
}))
class Test extends React.Component {
componentDidMount() {
const { data, str } = this.props
console.log(data, str) // [1, 2, 3] 'model-state'
}
}
effects
effects用于处理异步操作和业务逻辑,不能直接修改state,由action触发,可以触发action,可以和服务器进行交互,可以获取全局state的数据
effects 有三种属性 put, call, select
put
put 用于触发action
// model/test.js
const TestModel = {
namespace: "test",
state: {
data: [1, 2, 3]
},
effects: {
* fetch({ payload }, { put }) {
yield put({
type: 'setData'
})
// [1, 2, 3]
},
* update({ payload }, { put }) {
yield put({
type: 'fetch'
})
// [1, 2, 3]
}
/**
* yield put() 触发action 可以调用reducers和effects中的方法
*/
},
reducers: {
setData(state, { payload }) {
console.log(payload)
return {
...state,
data: payload
}
}
}
};
export default TestModel
call
call 用于调用异步逻辑,支持promise
// model/test.js
import { queryList } from '@/services/test';
const TestModel = {
namespace: "test",
state: {},
effects: {
* fetch({ payload }, { call }) {
const data = yield call(queryList, payload)
console.log(data) // data为queryList请求的返回结果
/**
* yield call()
* 第一个参数为异步操作请求函数
* 第二个参数为函数所传递的值
*/
}
}
};
export default TestModel
select
select 用于从state获取数据
// model/test.js
const TestModel = {
namespace: "test",
state: {
data: [1, 2, 3]
},
effects: {
* fetch({ payload }, { select }) {
const data = yield select(state => state.test.data)
console.log(data) // [1, 2, 3]
}
/**
* 通过select 获取state中的数据, test为namespace名
*/
}
};
export default TestModel
put与put.resolve
yield put直接调用reducers,堵塞 => 同步
yield put调用非reducers,非堵塞 => 异步
yield put.resolve调用,堵塞 => 同步
export default const TestModel = {
effects: {
* asyncInitData({ payload }, { put }) {
yield put({
type: 'initData',
payload: payload
})
},
// 使用yield put 调用reducers
* reducersBtn(_, { put }) {
console.log('1')
yield put({
type: "initData",
payload: '2'
})
console.log('3')
// 打印结果为 1, 2, 3
},
// 使用yield put 调用effects
* button(_, { put }) {
console.log('1')
yield put({
type: 'asyncInitData',
payload: '2'
})
console.log('3')
// 打印结果为 1, 3, 2
},
// 使用yield put.resolve 调用effects
* resolveBtn(_, { put }) {
console.log('1')
yield put.resolve({
type: 'asyncInitData',
payload: '2'
})
console.log('3')
// 打印结果为 1, 2, 3
}
},
reducers: {
initData(state, { payload }) {
console.log(payload)
return {
...state,
...payload
}
}
}
}
callback和Promise
在effects中定义用于处理异步操作和业务逻辑的方法,我们可以通过callback或者promise获取一个返回值
(state的值无法即使更新时可以使用)
callback
// model.js
export default const TestModel = {
namespace: 'test',
effects: {
* query({ payload, callback }, { call }) {
const response = yield call(query, payload)
if (callback) callback(response)
}
}
}
// test.jsx
import React, { useEffect } from 'react';
const Test = (props) => {
useEffect(() => {
dispatch({
type: 'test/query',
callback: (res) => {
console.log(res)
}
}).then(res => {
console.log('queryCbd-res: ', res)
})
})
}
**
Promise
effects中的方法会return 返回一个Promise对象
// model.jsexport default { namespace: 'test', effects: { * query({ payload }, { call }) { const response = yield call(query, payload) return new Promise(function(resolve, reject){ if (response.code == 200) { resolve(response.data) } reject(response) }) // 或者写成return response effects会return一个proimse对象 //return response } },}
// test.jsximport React, { useEffect } from 'react';const Test = (props) => { useEffect(() => { dispatch({ type: 'test/query' }).then(res => { console.log('test/query: ', res) }) })}
reducers
reducers 用于处理同步操作,唯一可以修改state的地方,有action触发
// model/test.jsconst TestModel = { namespace: "test", state: { data: [1, 2, 3] }, effects: { * fetch(_, { put, select }) { const data = yield select(state => state.test.data) yield put({ type: 'setData', payload: data }) const newData = yield select(state => state.test.data) console.log(newData) // [2, 4, 6] } }, reducers: { // 在reducers中进行一些数据的修改, 并返回更新state中的数据 setData(state, { payload }) { return { ...state, data: payload.map(item => item * 2) } }, /** setData(state, action) { const { payload } = action return { ...state, data: payload.map(item => item * 2) } } */ }};export default TestModel
同时我们在页面也可以通过dispatch对触发reducers去修改state中得数据源
import React, { useEffect } from 'react';const Test = (props) => { useEffect(() => { dispatch({ type: 'test/setData', payload: [2, 4, 6] }) })}
subscriptions
subscription 是订阅,用于订阅一个数据源,根据需要dispath相应的action,在app.start()时被执行
数据源可以是当前的时间,服务器的websocket链接,keyboard输入(鼠标,键盘),geolocation变化,history路由变化等
// model/test.jsimport key from 'keymaster'; // Keymaster是一个简单的微型库,用于在Web应用程序中定义和分发键盘快捷键const TestModel = { namespace: "test", state: {}, effects: {}, reducers: {}, subscriptions: { /** * 这里的setup方法可以随便命名, * 当监听有变化时就会依次执行 * dispatch和history 如文档中一样 * dispatch 可以调用effects与reducers中的方法 * history 获取当前路由的信息 */ setup({ dispatch, history }) { // 监听当前浏览器的页面大小变化, 页面发生改变时就会触发dispatch方法 window.onresize = (e) => { // dispatch({ type: 'effects'}) console.log(e) } }, keyboard({ dispatch }) { // 监听当鼠标在页面中点击时触发 document.addEventListener('click',() => { console.log('鼠标点击时触发') }) }, keyEvent({ dispatch }) { // 监听ctrl + up键点击时触发 key('ctrl+up', () => { console.log('ctrl+up') }) }, watchHistory({ dispatch, history }){ // 获取当前页面的所有路由信息 history.listen((location) => { console.log(location) }) } }};export default TestModel
connect 与 @connect
connect 的作用是将组件和models结合在一起,将models中的state数据绑定到组件的props中,并提供一些额外的功能,如dispatch
connect 方法返回的也是一个react组件
connect 方法传入的第一个参数是 mapStateToProps 函数,该函数需要返回一个对象,用于建立 State 到 Props 的映射关系
connect 接收一个函数,返回一个函数
connect()()
export default connect(({ user, login, global = {} }) => ({ userInfo: user.userInfo, login: login.userLogin, notices: global.notices}))(BasicLayout);// BasicLayout 为组件 class BasicLayout extends React.Component {}
@connect
@connect 为ES7中的语法( @Decorator 装饰器 )
@方法名 可以对一些对象进行装饰包装然后返回一个被包装过的对象,可以装饰的对象包括:类,属性,方法等
export default@connect(({ user, login, global = {} }) => ({ userInfo: user.userInfo, login: login.userLogin, notices: global.notices}))class BasicLayout extends React.Component {}