dva

React and redux based, lightweight and elm-style framework. (Inspired by choo)
Table of Contents
Features
- based on redux, redux-saga and react-router: stand on the shoulders of giants
- small api: only 5 methods, there's not a lot to learn
- elm cocepts: organize model with
reducers
, effects
and subscriptions
- mobile and react-native support: cross platform
- dynamic model and router: split large app and load on demand
- plugin system: make dva extendable, e.g. use dva-loading to avoid write
showLoading
and hideLoading
hundreds of times - hmr support with babel-plugin-dva-hmr
- support typescript: use dva-boilerplate-typescript for quicking start
Demos
Getting Started
This is how dva app organized, with only 5 api. View Count Example for more details.
import dva, { connect } from 'dva';
const app = dva();
app.use(plugin);
app.model(model);
const App = connect(mapStateToProps)(Component);
app.router(routes);
app.start('#root');
You can follow Getting Started to make a Count App
step by step.
Creating an App
We recommend to use dva-cli for boilerplating your app.
// Install dva-cli
$ npm install dva-cli -g
// Create app and start
$ dva new myapp
$ cd myapp
$ npm install
$ npm start
But if you like create-react-app, feel free to read Creating dva app with create-react-app.
Concepts
View Concepts for detail explain on Model, State, Action, dispatch, Reducer, Effect, Subscription, Router and Route Components.
API
app = dva(opts)
Initialize a new dva
app. Takes an optional object of handlers that is passed to app.use. Besides, you can config history
and initialState
here.
opts.history:
the history for router, default: hashHistory
opts.initialState:
initialState of the app, default: {}
If you want to use browserHistory
instead of hashHistory
:
import { browserHistory } from 'dva/router';
const app = dva({
history: browserHistory,
});
app.use(hooks)
Register an object of hooks on the application.
Support these hooks
:
onError(fn):
called when an effect
or subscription
emit an erroronAction(array|fn):
called when an action
is dispatched, used for registering redux middleware, support Array
for convenienceonStateChange(fn):
called after a reducer changes the state
onReducer(fn):
used for apply reducer enhanceronEffect(fn):
used for wrapping effect to add custom behavior, e.g. dva-loading for automatical loading stateonHmr(fn):
used for hot module replacementextraReducers(object):
used for adding extra reducers, e.g. redux-form needs extra form
reducer
app.model(obj)
Create a new model. Takes the following arguments:
- namespace: namespace the model
- state: initial value
- reducers: synchronous operations that modify state. Triggered by
actions
. Signature of (state, action) => state
, same as Redux. - effects: asynchronous operations that don't modify state directly. Triggered by
actions
, can call actions
. Signature of (action, { put, call, select })
, - subscriptions: asynchronous read-only operations that don't modify state directly. Can call
actions
. Signature of ({ dispatch, history })
.
put(action) in effects, and dispatch(action) in subscriptions
Send a new action to the models. put
in effects is the same as dispatch
in subscriptions.
e.g.
yield put({
type: actionType,
payload: attachedData,
error: errorIfHave
});
or
dispatch({
type: actionType,
payload: attachedData,
error: errorIfHave
});
When dispatch action inside a model
, we don't need to add namespace prefix. And if ouside a model
, we should add namespace separated with a /
, e.g. namespace/actionType
.
call(asyncFunction)
Call async function. Support promise.
e.g.
const result = yield call(api.fetch, { page: 1 });
select(function)
Select data from global state.
e.g.
const count = yield select(state => state.count);
A typical model example:
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: {
keyboard({ dispatch }) {
return key('ctrl+up', () => { dispatch({ type: 'addDelay'}); });
},
},
});
And another complex model example from dva-hackernews.
app.router(({ history }) => routes)
Config router. Takes a function with arguments { history }
, and expects router
config. It use the same api as react-router, return jsx elements or JavaScript Object for dynamic routing.
e.g.
import { Router, Route } from 'dva/routes';
app.router(({ history } => ({
<Router history={ history }>
<Route path="/" component={App} />
</Router>
});
More on react-router/docs.
app.start(selector?)
Start the application. selector
is optional. If no selector
arguments, it will return a function that return JSX elements.
Installation
$ npm install dva
FAQ
Why is it called dva?
dva is a hero from overwatch. She is beautiful and cute, and dva
is the shortest and available one on npm when creating it.
Which packages was dva built on?
Is it production ready?
Sure.
Does it support IE8?
No.
Does it support react-native?
Yes. Try to get started with dva-example-react-native.
Read More
License
MIT