创建项目
npx create-react-app 6.connected-react-routercd 6.connected-react-routercnpm i -S react-router-dom redux react-redux connected-react-router
简单示例
使用步骤
- 1、引入 Router 容器
import {ConnectedRouter} from 'connected-react-router' - 2、引入 中间件
import {routerMiddleware} from 'connected-react-router'; - 3、引入 reducer
import {connectRouter} from 'connected-react-router'; - 4、push
import {push} from 'connected-react-router';
src/index.js
import React from 'react';import ReactDOM from 'react-dom';import {Route, Link} from 'react-router-dom';import {Provider} from 'react-redux';// ConnectedRouter 类似 HashRouter BrowserRouter// 它可以实现监听路径变化的功能,当路径发生变化后,会派发特定的actionimport {ConnectedRouter} from 'connected-react-router';import history from './history';import store from './store';import Home from './components/Home';import Counter from './components/Counter';function App(){ return ( <Provider store={store}> <ConnectedRouter history={history}> <div> <ul> <li><Link to="/" exact>首页</Link></li> <li><Link to="/counter">计数器</Link></li> </ul> <Route path="/" exact component={Home} /> <Route path="/counter" component={Counter} /> </div> </ConnectedRouter> </Provider> )}ReactDOM.render(<App />, document.getElementById('root'));
src/history.js
import {createBrowserHistory} from 'history'let history = createBrowserHistory();export default history;
src/store/index.js
import {createStore, applyMiddleware} from 'redux';import history from '../history';import {routerMiddleware} from 'connected-react-router';import rootReducer from './reducers';const store = applyMiddleware(routerMiddleware(history))(createStore)(rootReducer);export default store;/** * routerMiddleware 作用是拦截跳转路径的 action,进行路径跳转 * store.dispatch 会判断你是否要跳转路径,如果是的话,用history.push来跳,如果不是的话,next */
reducers
src/store/reducers/index.js
import {combineReducers} from 'redux';// 这个reducer可以识别ConnectedRouter派发的action,把这个action里面对应的路径信息存入store里// store.getState().router.locationimport {connectRouter} from 'connected-react-router';import history from '../../history';import counter from './counter';const reducers = { counter, router: connectRouter(history),}const rootReducer = combineReducers(reducers);export default rootReducer;
src/store/reducers/counter.js
import * as types from '../action-types';function counter(state={number:0}, action){ switch (action.type) { case types.ADD: return {number: state.number+1} case types.MINUS: return {number: state.number-1} default: return state; }}export default counter;
actions
src/store/action-types.js
export const ADD = 'ADD';export const MINUS = 'MINUS';
src/store/actions/counter.js
import * as types from '../action-types';import {push} from 'connected-react-router'; //push是一个方法,会返回一个actionconst actions = { add(){ return {type: types.ADD}; }, minus(){ return {type: types.MINUS}; }, go(path){ return push(path); },}export default actions;
components
src/components/Home.js
import React from 'react';export default class Home extends React.Component { render(){ return ( <div> <h1>Home</h1> <button onClick={() => this.props.history.goBack()}>返回</button> </div> ) }}
src/components/Counter.js
import React from 'react';import {connect} from 'react-redux';import actions from '../store/actions/counter'class Counter extends React.Component { render(){ let ps = this.props; return ( <div> <p>{ps.number}</p> <button onClick={ps.add}>+</button> <button onClick={ps.minus}>-</button> <button onClick={() => ps.go('/')}>跳转到home</button> </div> ) }}export default connect(state => state.counter, actions)(Counter)
源码实现
connected-react-router/index.js
export {default as ConnectedRouter} from './ConnectedRouter';export {default as routerMiddleware} from './routerMiddleware';export {default as connectRouter} from './connectRouter';export {push, replace, go, goBack, goForward} from './actions';export {CALL_HISTORY_METHOD, LOCATION_CHANGE} from './action-types';
connected-react-router/action-types.js
// 调用历史对象上的方法export const CALL_HISTORY_METHOD = 'CALL_HISTORY_METHOD';// 向仓库派发动作,请求修改路径信息export const LOCATION_CHANGE = 'LOCATION_CHANGE';
connected-react-router/actions.js
import * as types from './action-types';// 路径变化的 actionCreatorexport function onLocationChanged (location, action){ return { type: types.LOCATION_CHANGE, payload: { location, action, } }}// 跳转路径的actionCreatorfunction updateLocation(method){ // props.history[method](...args) return (...args) => ({ type: types.CALL_HISTORY_METHOD, // 调用历史对象中的方法 payload: { //携带额外的数据 method, args, } })}export const push = updateLocation('push');export const replace = updateLocation('replace');export const go = updateLocation('go');export const goBack = updateLocation('goBack');export const goForward = updateLocation('goForward');
connected-react-router/ConnectedRouter.js
import React from 'react';import {connect, ReactReduxContext} from 'react-redux';import {Router} from 'react-router-dom';import {onLocationChanged } from './actions'class ConnectedRouter extends React.Component { componentDidMount(){ let ps = this.props; // 添加监听,当路径发生变化的时候,会执行回调,并传递最新的location和action this.unlisten = ps.history.listen((location, action) => { ps.dispatch(onLocationChanged (location, action)); }); } componentWillUnmount() { this.unlisten(); } render(){ const {history, children} = this.props; return ( <Router history={history}> {children} </Router> ) }}export default connect()(ConnectedRouter);
connected-react-router/routerMiddleware.js
import {CALL_HISTORY_METHOD} from './action-types';function routerMiddleware(history){ // 返回一个处理跳转的中间件 return store => next => action => { // 如果不是要跳转路径,直接下一步 if (action.type !== CALL_HISTORY_METHOD){ return next(action); } const {payload: {method, args}} = action; history[method](...args); }}export default routerMiddleware;
connected-react-router/connectRouter.js
import {LOCATION_CHANGE} from './action-types';function connectRouter(history){ const initialState = { location: history.location, action: history.action, } return function(state=initialState, action){ if (action.type === LOCATION_CHANGE){ return {...state, ...action.payload}; } else { return state; } }}export default connectRouter;