1.生成项目 #

create-react-app zhufeng_redux_first_history
cd zhufeng_redux_first_history
npm install redux react-redux  react-router-dom redux-first-history --save

2.跑通项目 #

2.1 src\index.js #

src\index.js

//导入React 库
import React from 'react';
//从react-dom/client 导入 createRoot 方法
import { createRoot } from 'react-dom/client';
//从react-router - dom 导入 Route, Routes 和 Link 组件
import { Route, Routes, Link } from "react-router-dom";
//导入自定义的 HistoryRouter 组件
import { HistoryRouter } from "./redux-first-history/rr6";
//从react-redux 导入 Provider 组件
import { Provider } from 'react-redux';
//从./store 导入 store 和 history 对象
import { store, history } from "./store";
//导入 Home 组件
import Home from './components/Home';
//导入 Counter 组件
import Counter from './components/Counter';
//找到页面中的根元素,并创建一个 React root
createRoot(document.getElementById('root'))
    .render(
        // 使用 Provider 组件将 Redux store 传递给应用程序
        <Provider store={store}>
            {/*使用自定义的 HistoryRouter 组件和 history 对象 */}
            <HistoryRouter history={history}>
                {/*创建导航菜单*/}
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/counter">Counter</Link></li>
                </ul>
                {/*定义路由规则*/}
                <Routes>
                    <Route path="/" element={<Home />} />
                    <Route path="/counter" element={<Counter />} />
                </Routes>
            </HistoryRouter>
        </Provider >
    );

2.2 history.js #

src\history.js

// 从 'history' 库中导入 createBrowserHistory 函数
import { createBrowserHistory } from 'history';
// 从 "./redux-first-history" 中导入 createReduxHistoryContext 函数
import { createReduxHistoryContext } from "./redux-first-history";
// 创建一个 browserHistory 实例
const history = createBrowserHistory();
// 使用 createReduxHistoryContext 函数和 browserHistory 实例创建 Redux 相关对象
const { routerReducer, routerMiddleware, createReduxHistory } = createReduxHistoryContext({ history });
// 导出 routerReducer, routerMiddleware 和 createReduxHistory
export {
    routerReducer,
    routerMiddleware,
    createReduxHistory
}

2.3 store\index.js #

src\store\index.js

// 从 'redux' 库中导入 createStore 和 applyMiddleware 函数
import { legacy_createStore as createStore, applyMiddleware } from 'redux';
// 导入我们的组合 reducer
import combinedReducer from './reducers';
// 从 '../history' 中导入 routerMiddleware 和 createReduxHistory 函数
import { routerMiddleware, createReduxHistory } from '../history';
// 创建一个 Redux store,并应用 routerMiddleware 中间件
export const store = applyMiddleware(routerMiddleware)(createStore)(combinedReducer);
// 将 store 绑定到 window 对象,以便在浏览器控制台进行调试
window.store = store;
// 使用 store 创建一个与 Redux 同步的 history 对象
export const history = createReduxHistory(store);

2.4 action-types.js #

src\store\action-types.js

// 定义一个常量 ADD,用于表示加法操作
export const ADD = 'ADD';
// 定义一个常量 MINUS,用于表示减法操作
export const MINUS = 'MINUS';

2.5 counter.js #

src\store\reducers\counter.js

// 从 '../action-types' 中导入所有的 action 类型
import * as actionTypes from '../action-types';
// 定义一个 counter 函数作为 reducer
function counter(state = { number: 0 }, action) {
    // 根据 action 的类型来更新状态
    switch (action.type) {
        // 当 action 类型为 ADD 时,将 number 增加 1
        case actionTypes.ADD:
            return { number: state.number + 1 };
        // 当 action 类型为 MINUS 时,将 number 减少 1
        case actionTypes.MINUS:
            return { number: state.number - 1 };
        // 如果 action 类型未匹配,返回原始状态
        default:
            return state;
    }
}
// 导出 counter reducer
export default counter;

2.6 reducers\index.js #

src\store\reducers\index.js

// 从 'redux' 库中导入 combineReducers 函数
import { combineReducers } from 'redux';
// 导入 counter reducer
import counter from './counter';
// 从 '../../history' 中导入 routerReducer
import { routerReducer } from '../../history';
// 将所有的 reducer 组合成一个对象
const reducers = {
    counter,
    router: routerReducer
}
// 使用 combineReducers 函数将这些 reducer 合并成一个,并导出
export default combineReducers(reducers);

2.7 Home.js #

src\components\Home.js

// 导入 React 库
import React from 'react';
// 从 'react-redux' 库中导入 useDispatch 函数
import { useDispatch } from 'react-redux';
// 从 "../redux-first-history" 中导入 push 函数
import { push } from "../redux-first-history";
// 定义一个 Home 组件
function Home() {
    // 使用 useDispatch 获取 dispatch 函数
    const dispatch = useDispatch();
    // 定义一个跳转到 /counter 路径的函数
    const gotoCounter = () => {
        dispatch(push('/counter'));
    }
    // 返回包含一个按钮的 JSX,点击按钮时跳转到 /counter 路径
    return (
        <div>
            <p>Home</p>
            <button onClick={gotoCounter}>跳转到/counter</button>
        </div>
    )
}
// 导出 Home 组件
export default Home;

2.9 Counter.js #

src\components\Counter.js

function Counter() {
    return (
        <div>
            <p>Counter</p>
        </div>
    )
}
export default Counter;

3.实现 #

3.1 redux-first-history\index.js #

src\redux-first-history\index.js

// 从 './actions' 文件中导出 push 函数
export { push } from './actions';
// 从 './create' 文件中导出 createReduxHistoryContext 函数
export { createReduxHistoryContext } from './create';

3.2 actions.js #

src\redux-first-history\actions.js

// 定义一个常量 CALL_HISTORY_METHOD,表示调用历史方法的操作
export const CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD';
// 定义一个常量 LOCATION_CHANGE,表示位置变更的操作
export const LOCATION_CHANGE = '@@router/LOCATION_CHANGE';
// 定义一个 locationChangeAction 函数,用于创建位置变更的动作对象
export function locationChangeAction(location, action) {
    return {
        type: LOCATION_CHANGE,
        payload: {
            action,
            location
        }
    };
}
// 定义一个 updateLocation 函数,用于生成更新位置的操作对象
function updateLocation(method) {
    return (...args) => {
        return {
            type: CALL_HISTORY_METHOD,
            payload: {
                method,
                args
            }
        };
    };
}
// 使用 updateLocation 函数创建 push 操作对象
export const push = updateLocation('push');
// 使用 updateLocation 函数创建 replace 操作对象
export const replace = updateLocation('replace');

3.3 create.js #

src\redux-first-history\create.js

// 导入 createRouterMiddleware 函数
import { createRouterMiddleware } from './middleware';
// 导入 push、replace 和 locationChangeAction 函数
import { push, replace, locationChangeAction } from './actions';
// 导入 createRouterReducer 函数
import { createRouterReducer } from './reducer';
// 定义一个 createReduxHistoryContext 函数,用于创建 Redux 和 History 相关的上下文
export function createReduxHistoryContext({
    history
}) {
    // 创建 routerMiddleware
    const routerMiddleware = createRouterMiddleware(history);
    // 创建 routerReducer
    const routerReducer = createRouterReducer(history);
    // 定义一个 createReduxHistory 函数,用于在 store 中创建一个与 Redux 同步的 History 对象
    function createReduxHistory(store) {
        // 初始化时分发 locationChangeAction
        store.dispatch(locationChangeAction(history.location, history.action));
        // 监听历史变更并分发 locationChangeAction
        history.listen(({
            location,
            action
        }) => {
            store.dispatch(locationChangeAction(location, action));
        });
        // 返回一个包含与 Redux 同步的方法的对象
        return {
            createHref: history.createHref,
            push: (...args) => store.dispatch(push(...args)),
            replace: (...args) => store.dispatch(replace(...args)),
            listen: history.listen,
            get location() {
                return store.getState().router.location;
            },
            get action() {
                return store.getState().router.action;
            }
        };
    }
    // 返回一个包含 routerMiddleware、createReduxHistory 和 routerReducer 的对象
    return {
        routerMiddleware,
        createReduxHistory,
        routerReducer
    };
}

3.4 middleware.js #

src\redux-first-history\middleware.js

// 从 './actions' 中导入 CALL_HISTORY_METHOD 常量
import { CALL_HISTORY_METHOD } from './actions';
// 定义一个 createRouterMiddleware 函数,用于创建路由中间件
export function createRouterMiddleware(history) {
    // 返回一个中间件
    return function () {
        // 返回一个处理下一个动作的函数
        return function (next) {
            // 返回一个处理当前动作的函数
            return function (action) {
                // 如果动作类型不是 CALL_HISTORY_METHOD,将动作传递给下一个中间件
                if (action.type !== CALL_HISTORY_METHOD) {
                    return next(action);
                }
                // 如果动作类型是 CALL_HISTORY_METHOD,处理并调用 history 对象的方法
                const { method, args } = action.payload;
                history[method](...args);
            }
        }
    }
}

3.5 reducer.js #

src\redux-first-history\reducer.js

// 从 './actions' 文件中导入 LOCATION_CHANGE 常量
import { LOCATION_CHANGE } from './actions';
// 定义一个 createRouterReducer 函数,用于创建路由 Reducer
export function createRouterReducer(history) {
    // 定义初始状态
    const initialState = {
        action: history.action,
        location: history.location
    }
    // 返回一个根据动作类型更新状态的函数
    return function (state = initialState, action) {
        // 如果动作类型是 LOCATION_CHANGE,则更新状态
        if (action.type === LOCATION_CHANGE) {
            return { ...state, location: action.payload.location, action: action.payload.action };
        } else {
            // 否则保持原状态
            return state;
        }
    }
}

3.6 rr6\index.js #

src\redux-first-history\rr6\index.js

// 导入 React
import React from 'react';
// 导入 react-router 的 Router 组件
import { Router } from 'react-router';
// 定义一个 HistoryRouter 组件
export function HistoryRouter({ history, children }) {
    // 使用 useState 创建一个 state,存储当前历史记录的动作和位置
    const [state, setState] = React.useState({
        action: history.action,
        location: history.location
    });
    // 使用 useLayoutEffect 在组件挂载时添加一个监听器
    React.useLayoutEffect(() => {
        history.listen(setState);
    }, [history]);
    // 渲染一个 Router 组件,传递必要的属性
    return (
        <Router
            location={state.location}
            action={state.action}
            navigator={history}
            navigationType={state.action}
        >
            {children}
        </Router>
    )
}