/dva

Lightweight elm-style framework based on react and redux.

Primary LanguageJavaScript

dva

NPM version Build Status Coverage Status NPM downloads

Lightweight elm-style framework based on react and redux.


Documents

基础:

扩展阅读:

Features

  • based on redux, redux-saga and react-router
  • small api: only 5 methods
  • transparent side effects: using effects and subscriptions brings clarity to IO
  • mobile and react-native support: don't need router
  • dynamic model and router: split large scale app on demand
  • plugin system: with hooks
  • hmr support: components and routes is ready

Demos

Getting Started

Install

$ npm install --save dva

Example

Let's create an count app that changes when user click the + or - button.

import React from 'react';
import dva, { connect } from 'dva';
import { Router, Route } from 'dva/router';

// 1. Initialize
const app = dva();

// 2. Model
app.model({
  namespace: 'count',
  state: 0,
  reducers: {
    add  (count) { return count + 1 },
    minus(count) { return count - 1 },
  },
});

// 3. View
const App = connect(({ count }) => ({
  count
}))(function(props) {
  return (
    <div>
      <h2>{ props.count }</h2>
      <button key="add" onClick={()   => { props.dispatch({type: 'count/add'})}}>+</button>
      <button key="minus" onClick={() => { props.dispatch({type: 'count/minus'})}}>-</button>
    </div>
  );
});

// 4. Router
app.router(({ history }) =>
  <Router history={history}>
    <Route path="/" component={App} />
  </Router>
);

// 5. Start
app.start('#root');

API

app = dva(opts)

Initialize a new dva app. opts 里除 historyinitialState 外会被传递给 app.use .

  • opts.history: default: hashHistory
  • opts.initialState: default: {}

opts.history 是给路由用的 history,支持 hashHistory 和 browserHistory 。默认 hashHistory,要换成 browserHistory 可以这样:

import { browserHistory } from 'dva/router';
const app = dva({
  history: browserHistory,
});

opts.initialState 是给 store 的初始值,优先级高于 model 里的 state 。

app.use(hooks)

dva 的插件机制是通过 hooks 实现的,用于添加自定义行为和监听器。

目前支持以下 hooks :

  • onError(err => {}): effects 和 subscriptions 出错时触发
  • onAction(Array|Function): 等同于 redux middleware,支持数组
  • onStateChange(listener): 绑定 listner,state 变化时触发
  • onReducer(reducerEnhancer): 应用全局的 reducer enhancer,比如 redux-undo
  • onHmr(render => {}): 提供 render 方法用于重新渲染 routes 和 components,暂还不支持 model
  • extraReducers(obj): 提供额外的 reducers,比如 redux-form 需要全局 reducer form

app.model(obj)

Create a new model. Takes the following arguments:

  • namespace: 通过 namespace 访问其他 model 上的属性,不能为空
  • state: 初始值
  • reducers: 同步操作,用于更新数据,由 action 触发
  • effects: 异步操作,处理各种业务逻辑,不直接更新数据,由 action 触发,可以 dispatch action
  • subscriptions: 异步只读操作,不直接更新数据,可以 dispatch action

一个典型的 model :

app.model({
  namespace: 'count',
  state: 0,
  reducers: {
    add(state) { return state + 1; },
    minus(state) { return state - 1; },
  },
  effects: {
    *addDelay(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'add' });
    },
  },
  subscriptions: {
    // 监听键盘事件,在点击 ctrl + up 时,触发 addDelay action
    keyboard({ dispatch }) {
      return key('ctrl+up', () => { dispatch({ type: 'addDelay'}); });
    },
  },
});

reducers 来自 redux,格式为 (state, action) => state,详见 Reducers@redux.js.org,但不支持 combineReducer 。

effects 是 side effects,用于存放异步逻辑,底层引入了 redux-sagas 做异步流程控制,通过 generator 把异步转换成同步写法。格式为 *(action, effects) => {}

subscriptions 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 ({ dispatch, history }) => unsubscribe

app.router(({ history }) => routes)

创建路由。不做封装,使用和 react-router 相同的配置,可用 jsx 格式,也可用 javascript object 的格式支持动态路由。

详见:react-router/docs

app.start(selector?)

Start the application. 如果没有传入 selector,则返回 React Element,可用于 SSR,react-native, 国际化等等。

FAQ

Why is it called dva?

dva is a hero from overwatch. She is cute, and dva is the shortest one that is available on npm.

Is it production ready?

Yes.

License

MIT