Appearance
支持路由
本节目标:理解“支持路由”的核心思路,并能把它用于实际开发或面试表达。
5.
5.1使用
5.1.1 src\index.js
src\index.js
import React from 'react';import dva, { connect } from './dva';+import { Router, Route,Link } from './dva/router';const app = dva();app.model({ namespace: 'counter', state: { number: 0 }, reducers: { add(state) { return { number: state.number + 1 }; } }, effects: { *asyncAdd(action, { call, put }) { yield call(delay, 1000); yield put({ type: 'counter/add' }); } }});function Counter(props) { return ( <div> <p>{props.number}</p> <button onClick={() => props.dispatch({ type: "counter/add" })}>+</button> <button onClick={() => props.dispatch({ type: "counter/asyncAdd" })}>异步
+</button> </div> )}const ConnectedCounter = connect( (state) => state.counter)(Counter);+const Home = () => <div>Home</div>+app.router((api) => (+ <Router history={api.history}>+ <>+ <Link to="/">Home</Link>+ <Link to="/counter">Counter</Link>+ <Route path="/" exact={true} component={Home} />+ <Route path="/counter" component={ConnectedCounter} />+ </>+ </Router>+));app.start('#root');
function delay(ms) { return new Promise((resolve) => { setTimeout(function () { resolve(); }, ms); });}
5.2实现
5.2.1 dva\index.js
src\dva\index.js
import React from 'react';import ReactDOM from 'react-dom';import { createStore, combineReducers, applyMiddleware } from 'redux';import createSagaMiddleware from 'redux-saga';import * as sagaEffects from 'redux-saga/effects';import { NAMESPACE_SEP } from './constants';import { connect, Provider } from 'react-redux';import prefixNamespace from './prefixNamespace';+import { createHashHistory } from 'history';+let history = createHashHistory();export { connect };
function dva() { const app = { _models: [], model, router, _router: null, start } const initialReducers = {}; function model(model) { const prefixedModel = prefixNamespace(model); app._models.push(prefixedModel); return prefixedModel; } function router(router) { app._router = router; }
function start(root) { for (const model of app._models) { initialReducers[model.namespace] = getReducer(model); } let rootReducer = createReducer(); const sagas = getSagas(app); const sagaMiddleware = createSagaMiddleware(); let store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); sagas.forEach(saga => sagaMiddleware.run(saga));+ ReactDOM.render(<Provider store={store}>{app._router({ history })}</Provider>, document.querySelector(root))function createReducer() { return combineReducers(initialReducers); } } function getSagas(app) { let sagas = []; for (const model of app._models) { sagas.push(getSaga(model.effects, model)); } return sagas; } return app;}function getSaga(effects, model) { return function* () { for (const key in effects) { const watcher = getWatcher(key, model.effects[key], model); yield sagaEffects.fork(watcher); } };}function getWatcher(key, effect, model) { return function* () { yield sagaEffects.takeEvery(key, function* sagaWithCatch(...args) { yield effect(...args, { ...sagaEffects, put: action => sagaEffects.put({ ...action, type: prefixType(action.type, model) }) }); }); };}function prefixType(type, model) { if (type.indexOf('/') === -1) {return `${model.namespace}${NAMESPACE_SEP}${type}`; } return type;}function getReducer(model) { let { reducers, state: defaultState } = model; let reducer = (state = defaultState, action) => { let reducer = reducers[action.type]; if (reducer) { return reducer(state, action); } return state; } return reducer;}
export default dva;
5.2.2 router.js
src\dva\router.js
export * from 'react-router-dom';