• https://github.com/supasate/connected-react-router
  • 它是一个把 redux 和 路由 连接在一起的库
  • 1、可以通过派发动作方式跳转路径
  • 2、可以在仓库里获取最新路径信息。store.getState().router里存放 location、action

创建项目

  1. npx create-react-app 6.connected-react-router
  2. cd 6.connected-react-router
  3. cnpm 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

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import {Route, Link} from 'react-router-dom';
  4. import {Provider} from 'react-redux';
  5. // ConnectedRouter 类似 HashRouter BrowserRouter
  6. // 它可以实现监听路径变化的功能,当路径发生变化后,会派发特定的action
  7. import {ConnectedRouter} from 'connected-react-router';
  8. import history from './history';
  9. import store from './store';
  10. import Home from './components/Home';
  11. import Counter from './components/Counter';
  12. function App(){
  13. return (
  14. <Provider store={store}>
  15. <ConnectedRouter history={history}>
  16. <div>
  17. <ul>
  18. <li><Link to="/" exact>首页</Link></li>
  19. <li><Link to="/counter">计数器</Link></li>
  20. </ul>
  21. <Route path="/" exact component={Home} />
  22. <Route path="/counter" component={Counter} />
  23. </div>
  24. </ConnectedRouter>
  25. </Provider>
  26. )
  27. }
  28. ReactDOM.render(<App />, document.getElementById('root'));

src/history.js

  1. import {createBrowserHistory} from 'history'
  2. let history = createBrowserHistory();
  3. export default history;

src/store/index.js

  1. import {createStore, applyMiddleware} from 'redux';
  2. import history from '../history';
  3. import {routerMiddleware} from 'connected-react-router';
  4. import rootReducer from './reducers';
  5. const store = applyMiddleware(routerMiddleware(history))(createStore)(rootReducer);
  6. export default store;
  7. /**
  8. * routerMiddleware 作用是拦截跳转路径的 action,进行路径跳转
  9. * store.dispatch 会判断你是否要跳转路径,如果是的话,用history.push来跳,如果不是的话,next
  10. */

reducers

src/store/reducers/index.js
  1. import {combineReducers} from 'redux';
  2. // 这个reducer可以识别ConnectedRouter派发的action,把这个action里面对应的路径信息存入store里
  3. // store.getState().router.location
  4. import {connectRouter} from 'connected-react-router';
  5. import history from '../../history';
  6. import counter from './counter';
  7. const reducers = {
  8. counter,
  9. router: connectRouter(history),
  10. }
  11. const rootReducer = combineReducers(reducers);
  12. export default rootReducer;

src/store/reducers/counter.js
  1. import * as types from '../action-types';
  2. function counter(state={number:0}, action){
  3. switch (action.type) {
  4. case types.ADD:
  5. return {number: state.number+1}
  6. case types.MINUS:
  7. return {number: state.number-1}
  8. default:
  9. return state;
  10. }
  11. }
  12. export default counter;

actions

src/store/action-types.js
  1. export const ADD = 'ADD';
  2. export const MINUS = 'MINUS';

src/store/actions/counter.js
  1. import * as types from '../action-types';
  2. import {push} from 'connected-react-router'; //push是一个方法,会返回一个action
  3. const actions = {
  4. add(){
  5. return {type: types.ADD};
  6. },
  7. minus(){
  8. return {type: types.MINUS};
  9. },
  10. go(path){
  11. return push(path);
  12. },
  13. }
  14. export default actions;

components

src/components/Home.js
  1. import React from 'react';
  2. export default class Home extends React.Component {
  3. render(){
  4. return (
  5. <div>
  6. <h1>Home</h1>
  7. <button onClick={() => this.props.history.goBack()}>返回</button>
  8. </div>
  9. )
  10. }
  11. }

src/components/Counter.js
  1. import React from 'react';
  2. import {connect} from 'react-redux';
  3. import actions from '../store/actions/counter'
  4. class Counter extends React.Component {
  5. render(){
  6. let ps = this.props;
  7. return (
  8. <div>
  9. <p>{ps.number}</p>
  10. <button onClick={ps.add}>+</button>
  11. <button onClick={ps.minus}>-</button>
  12. <button onClick={() => ps.go('/')}>跳转到home</button>
  13. </div>
  14. )
  15. }
  16. }
  17. export default connect(state => state.counter, actions)(Counter)

源码实现

connected-react-router/index.js

  1. export {default as ConnectedRouter} from './ConnectedRouter';
  2. export {default as routerMiddleware} from './routerMiddleware';
  3. export {default as connectRouter} from './connectRouter';
  4. export {push, replace, go, goBack, goForward} from './actions';
  5. export {CALL_HISTORY_METHOD, LOCATION_CHANGE} from './action-types';

connected-react-router/action-types.js

  1. // 调用历史对象上的方法
  2. export const CALL_HISTORY_METHOD = 'CALL_HISTORY_METHOD';
  3. // 向仓库派发动作,请求修改路径信息
  4. export const LOCATION_CHANGE = 'LOCATION_CHANGE';

connected-react-router/actions.js

  1. import * as types from './action-types';
  2. // 路径变化的 actionCreator
  3. export function onLocationChanged (location, action){
  4. return {
  5. type: types.LOCATION_CHANGE,
  6. payload: {
  7. location,
  8. action,
  9. }
  10. }
  11. }
  12. // 跳转路径的actionCreator
  13. function updateLocation(method){
  14. // props.history[method](...args)
  15. return (...args) => ({
  16. type: types.CALL_HISTORY_METHOD, // 调用历史对象中的方法
  17. payload: { //携带额外的数据
  18. method,
  19. args,
  20. }
  21. })
  22. }
  23. export const push = updateLocation('push');
  24. export const replace = updateLocation('replace');
  25. export const go = updateLocation('go');
  26. export const goBack = updateLocation('goBack');
  27. export const goForward = updateLocation('goForward');

connected-react-router/ConnectedRouter.js

  1. import React from 'react';
  2. import {connect, ReactReduxContext} from 'react-redux';
  3. import {Router} from 'react-router-dom';
  4. import {onLocationChanged } from './actions'
  5. class ConnectedRouter extends React.Component {
  6. componentDidMount(){
  7. let ps = this.props;
  8. // 添加监听,当路径发生变化的时候,会执行回调,并传递最新的location和action
  9. this.unlisten = ps.history.listen((location, action) => {
  10. ps.dispatch(onLocationChanged (location, action));
  11. });
  12. }
  13. componentWillUnmount() {
  14. this.unlisten();
  15. }
  16. render(){
  17. const {history, children} = this.props;
  18. return (
  19. <Router history={history}>
  20. {children}
  21. </Router>
  22. )
  23. }
  24. }
  25. export default connect()(ConnectedRouter);

connected-react-router/routerMiddleware.js

  1. import {CALL_HISTORY_METHOD} from './action-types';
  2. function routerMiddleware(history){
  3. // 返回一个处理跳转的中间件
  4. return store => next => action => {
  5. // 如果不是要跳转路径,直接下一步
  6. if (action.type !== CALL_HISTORY_METHOD){
  7. return next(action);
  8. }
  9. const {payload: {method, args}} = action;
  10. history[method](...args);
  11. }
  12. }
  13. export default routerMiddleware;

connected-react-router/connectRouter.js

  1. import {LOCATION_CHANGE} from './action-types';
  2. function connectRouter(history){
  3. const initialState = {
  4. location: history.location,
  5. action: history.action,
  6. }
  7. return function(state=initialState, action){
  8. if (action.type === LOCATION_CHANGE){
  9. return {...state, ...action.payload};
  10. } else {
  11. return state;
  12. }
  13. }
  14. }
  15. export default connectRouter;