React and redux based, lightweight and elm-style framework. (Inspired by choo)
- 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
andsubscriptions
- 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
andhideLoading
hundreds of times - hmr support with babel-plugin-dva-hmr
- support typescript: use dva-boilerplate-typescript for quicking start
This is how dva app organized, with only 5 api. View Count Example for more details.
import dva, { connect } from 'dva';
// 1. Create app
const app = dva();
// 2. Add plugins (optionally)
app.use(plugin);
// 3. Register models
app.model(model);
// 4. Connect components and models
const App = connect(mapStateToProps)(Component);
// 5. Config router with Components
app.router(routes);
// 6. Start app
app.start('#root');
You can follow Getting Started to make a Count App
step by step.
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.
View Concepts for detail explain on Model, State, Action, dispatch, Reducer, Effect, Subscription, Router and Route Components.
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,
});
Register an object of hooks on the application.
Support these hooks
:
onError(fn):
called when aneffect
orsubscription
emit an erroronAction(array|fn):
called when anaction
is dispatched, used for registering redux middleware, supportArray
for convenienceonStateChange(fn):
called after a reducer changes thestate
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 extraform
reducer
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 callactions
. 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: {
// Monitor keyboard input
keyboard({ dispatch }) {
return key('ctrl+up', () => { dispatch({ type: 'addDelay'}); });
},
},
});
And another complex model example from dva-hackernews.
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.
Start the application. selector
is optional. If no selector
arguments, it will return a function that return JSX elements.
$ npm install 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.
- views: react
- models: redux, react-redux, redux-saga
- router: react-router
- http: whatwg-fetch
Sure.
No.
Yes. Try to get started with dva-example-react-native.
- dva Knowledgemap - All knowledge points needed to create a dva app.
- dva 简介:Why dva and What's dva
- 教程:教你如何一步步完成一个中型应用
- 升级文档:Upgrade to 1.0.0
- 支付宝前端应用架构的发展和选择: 从 roof 到 redux 再到 dva
- React + Redux 最佳实践
- 从 0 开始实现 react 版本的 hackernews (基于 dva)