dva API

app = dva(opts)

创建应用,返回 dva 实例。(注:dva 支持多实例)

opts 包含:

  • history:指定给路由用的 history,默认是 hashHistory
  • initialState:指定初始数据,优先级高于 model 中的 state,默认是 {}如果要配置 history 为 browserHistory,可以这样:
  1. import createHistory from 'history/createBrowserHistory';
  2. const app = dva({
  3. history: createHistory(),
  4. });

另外,出于易用性的考虑,opts 里也可以配所有的 hooks ,下面包含全部的可配属性:

  1. const app = dva({
  2. history,
  3. initialState,
  4. onError,
  5. onAction,
  6. onStateChange,
  7. onReducer,
  8. onEffect,
  9. onHmr,
  10. extraReducers,
  11. extraEnhancers,
  12. });

app.use(hooks)

配置 hooks 或者注册插件。(插件最终返回的是 hooks )

比如注册 dva-loading 插件的例子:

  1. import createLoading from 'dva-loading';
  2. ...
  3. app.use(createLoading(opts));

hooks 包含:

onError((err, dispatch) => {})

effect 执行错误或 subscription 通过 done 主动抛错时触发,可用于管理全局出错状态。

注意:subscription 并没有加 try…catch,所以有错误时需通过第二个参数 done 主动抛错。例子:

  1. app.model({
  2. subscriptions: {
  3. setup({ dispatch }, done) {
  4. done(e);
  5. },
  6. },
  7. });

如果我们用 antd,那么最简单的全局错误处理通常会这么做:

  1. import { message } from 'antd';
  2. const app = dva({
  3. onError(e) {
  4. message.error(e.message, /* duration */3);
  5. },
  6. });

onAction(fn | fn[])

在 action 被 dispatch 时触发,用于注册 redux 中间件。支持函数或函数数组格式。

例如我们要通过 redux-logger 打印日志:

  1. import createLogger from 'redux-logger';
  2. const app = dva({
  3. onAction: createLogger(opts),
  4. });

onStateChange(fn)

state 改变时触发,可用于同步 state 到 localStorage,服务器端等。

onReducer(fn)

封装 reducer 执行。比如借助 redux-undo 实现 redo/undo :

  1. import undoable from 'redux-undo';
  2. const app = dva({
  3. onReducer: reducer => {
  4. return (state, action) => {
  5. const undoOpts = {};
  6. const newState = undoable(reducer, undoOpts)(state, action);
  7. // 由于 dva 同步了 routing 数据,所以需要把这部分还原
  8. return { ...newState, routing: newState.present.routing };
  9. },
  10. },
  11. });

onEffect(fn)

封装 effect 执行。比如 dva-loading 基于此实现了自动处理 loading 状态。

onHmr(fn)

热替换相关,目前用于 babel-plugin-dva-hmr

extraReducers

指定额外的 reducer,比如 redux-form 需要指定额外的 form reducer:

  1. import { reducer as formReducer } from 'redux-form'
  2. const app = dva({
  3. extraReducers: {
  4. form: formReducer,
  5. },
  6. });

extraEnhancers

指定额外的 StoreEnhancer ,比如结合 redux-persist 的使用:

  1. import { persistStore, autoRehydrate } from 'redux-persist';
  2. const app = dva({
  3. extraEnhancers: [autoRehydrate()],
  4. });
  5. persistStore(app._store);

app.model(model)

注册 model,详见 #Model 部分。

app.unmodel(namespace)

取消 model 注册,清理 reducers, effects 和 subscriptions。subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告⚠️。

app.replaceModel(model)

只在app.start()之后可用

替换model为新model,清理旧model的reducers, effects 和 subscriptions,但会保留旧的state状态,对于HMR非常有用。subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告⚠️。

如果原来不存在相同namespace的model,那么执行app.model操作

app.router(({ history, app }) => RouterConfig)

注册路由表。

通常是这样的:

  1. import { Router, Route } from 'dva/router';
  2. app.router(({ history }) => {
  3. return (
  4. <Router history={history}>
  5. <Route path="/" component={App} />
  6. </Router>
  7. );
  8. });

推荐把路由信息抽成一个单独的文件,这样结合 babel-plugin-dva-hmr 可实现路由和组件的热加载,比如:

  1. app.router(require('./router'));

而有些场景可能不使用路由,比如多页应用,所以也可以传入返回 JSX 元素的函数。比如:

  1. app.router(() => <App />);

app.start(selector?)

启动应用。selector 可选,如果没有 selector 参数,会返回一个返回 JSX 元素的函数。

  1. app.start('#root');

那么什么时候不加 selector?常见场景有测试、node 端、react-native 和 i18n 国际化支持。

比如通过 react-intl 支持国际化的例子:

  1. import { IntlProvider } from 'react-intl';
  2. ...
  3. const App = app.start();
  4. ReactDOM.render(<IntlProvider><App /></IntlProvider>, htmlElement);