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 concepts: organize model with
reducers,effectsandsubscriptions - 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
showLoadingandhideLoadinghundreds 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 startBut 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:hashHistoryopts.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,
});See user-dashboard example as a concrete usage of browserHistory.
Register an object of hooks on the application.
Support these hooks:
onError(fn):called when aneffectorsubscriptionemit an erroronAction(array|fn):called when anactionis dispatched, used for registering redux middleware, supportArrayfor convenienceonStateChange(fn):called after a reducer changes thestateonReducer(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 extraformreducer
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/router';
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 dvadva 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, react-router-redux
- http: isomorphic-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)