一、Reducer
reducer 是一个函数,接受 state 和 action,返回老的或新的 state 。即:(state, action) => state
增删改
以 todos 为例。
app.model({ ?namespace: ‘todos‘, ?state: [], ?reducers: { ???add(state, { payload: todo }) { ?????return state.concat(todo); ???}, ???remove(state, { payload: id }) { ?????return state.filter(todo => todo.id !== id); ???}, ???update(state, { payload: updatedTodo }) { ?????return state.map(todo => { ???????if (todo.id === updatedTodo.id) { ?????????return { ...todo, ...updatedTodo }; ???????} else { ?????????return todo; ???????} ?????}); ???}, ?},};
嵌套数据的增删改
建议最多一层嵌套,以保持 state 的扁平化,深层嵌套会让 reducer 很难写和难以维护。
app.model({ ?namespace: ‘app‘, ?state: { ???todos: [], ???loading: false, ?}, ?reducers: { ???add(state, { payload: todo }) { ?????const todos = state.todos.concat(todo); ?????return { ...state, todos }; ???}, ?},});
下面是深层嵌套的例子,应尽量避免。
app.model({ ?namespace: ‘app‘, ?state: { ???a: { ?????b: { ???????todos: [], ???????loading: false, ?????}, ???}, ?}, ?reducers: { ???add(state, { payload: todo }) { ?????const todos = state.a.b.todos.concat(todo); ?????const b = { ...state.a.b, todos }; ?????const a = { ...state.a, b }; ?????return { ...state, a }; ???}, ?},});
二、Effect
示例:
app.model({ ?namespace: ‘todos‘, ?effects: { ???*addRemote({ payload: todo }, { put, call }) { ?????yield call(addTodo, todo); ?????yield put({ type: ‘add‘, payload: todo }); ???}, ?},});
Effects
put
用于触发 action 。
yield put({ type: ‘todos/add‘, payload: ‘Learn Dva‘ });
call
用于调用异步逻辑,支持 promise 。
const result = yield call(fetch, ‘/todos‘);
select
用于从 state 里获取数据。
const todos = yield select(state => state.todos);
错误处理
全局错误处理
dva 里,effects 和 subscriptions 的抛错全部会走 onError
hook,所以可以在 onError
里统一处理错误。
const app = dva({ ?onError(e, dispatch) { ???console.log(e.message); ?},});
然后 effects 里的抛错和 reject 的 promise 就都会被捕获到了。
本地错误处理
如果需要对某些 effects 的错误进行特殊处理,需要在 effect 内部加 try catch
。
app.model({ ?effects: { ???*addRemote() { ?????try { ???????// Your Code Here ?????} catch(e) { ???????console.log(e.message); ?????} ???}, ?},});
异步请求
异步请求基于 whatwg-fetch,API 详见:https://github.com/github/fetch
GET 和 POST
import request from ‘../util/request‘;// GETrequest(‘/api/todos‘);// POSTrequest(‘/api/todos‘, { ?method: ‘POST‘, ?body: JSON.stringify({ a: 1 }),});
统一错误处理
假如约定后台返回以下格式时,做统一的错误处理。
{ ?status: ‘error‘, ?message: ‘‘,}
编辑 utils/request.js
,加入以下中间件:
function parseErrorMessage({ data }) { ?const { status, message } = data; ?if (status === ‘error‘) { ???throw new Error(message); ?} ?return { data };}
然后,这类错误就会走到 onError
hook 里。
三、Subscription
subscriptions
是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 ({ dispatch, history }) => unsubscribe
。
异步数据初始化
比如:当用户进入 /users
页面时,触发 action users/fetch
加载用户数据。
app.model({ ?subscriptions: { ???setup({ dispatch, history }) { ?????history.listen(({ pathname }) => { ???????if (pathname === ‘/users‘) { ?????????dispatch({ ???????????type: ‘users/fetch‘, ?????????}); ???????} ?????}); ???}, ?},});
path-to-regexp
Package
如果 url 规则比较复杂,比如 /users/:userId/search
,那么匹配和 userId 的获取都会比较麻烦。这是推荐用 path-to-regexp 简化这部分逻辑。
import pathToRegexp from ‘path-to-regexp‘;// in subscriptionconst match = pathToRegexp(‘/users/:userId/search‘).exec(pathname);if (match) { ?const userId = match[1]; ?// dispatch action with userId}
四、router
Config with JSX Element (router.js)
<Route path="/" component={App}> ?<Route path="accounts" component={Accounts}/> ?<Route path="statements" component={Statements}/></Route>
详见:react-router
Route Components
Route Components 是指 ./src/routes/
目录下的文件,他们是 ./src/router.js
里匹配的 Component。
通过 connect 绑定数据
比如:
import { connect } from ‘dva‘;function App() {}function mapStateToProps(state, ownProps) { ?return { ???users: state.users, ?};}export default connect(mapStateToProps)(App);
然后在 App 里就有了 dispatch
和 users
两个属性。
Injected Props (e.g. location)
Route Component 会有额外的 props 用以获取路由信息。
- location
- params
- children
更多详见:react-router
基于 action 进行页面跳转
import { routerRedux } from ‘dva/router‘;// Inside Effectsyield put(routerRedux.push(‘/logout‘));// Outside Effectsdispatch(routerRedux.push(‘/logout‘));// With queryrouterRedux.push({ ?pathname: ‘/logout‘, ?query: { ???page: 2, ?},});
除 push(location)
外还有更多方法,详见 react-router-redux
五、dva配置
Redux Middleware
比如要添加 redux-logger 中间件:
import createLogger from ‘redux-logger‘;const app = dva({ ?onAction: createLogger(),});
注:onAction 支持数组,可同时传入多个中间件。
history
切换 history 为 browserHistory
import { browserHistory } from ‘dva/router‘;const app = dva({ ?history: browserHistory,});
去除 hashHistory 下的 _k 查询参数
import { useRouterHistory } from ‘dva/router‘;import { createHashHistory } from ‘history‘;const app = dva({ ?history: useRouterHistory(createHashHistory)({ queryKey: false }),});
六、工具
通过 dva-cli 创建项目
先安装 dva-cli 。
$ npm install dva-cli -g
然后创建项目。
$ dva new myapp
最后,进入目录并启动。
$ cd myapp$ npm start
003-and design-dva.js 知识导图-01-Reducer,Effect,Subscription,Router,dva配置,工具
原文地址:https://www.cnblogs.com/bjlhx/p/9213862.html