create-react-app zhufeng_redux_first_history
cd zhufeng_redux_first_history
npm install redux react-redux react-router-dom redux-first-history --save
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 >
);
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
}
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);
src\store\action-types.js
// 定义一个常量 ADD,用于表示加法操作
export const ADD = 'ADD';
// 定义一个常量 MINUS,用于表示减法操作
export const MINUS = 'MINUS';
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;
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);
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;
src\components\Counter.js
function Counter() {
return (
<div>
<p>Counter</p>
</div>
)
}
export default Counter;
src\redux-first-history\index.js
// 从 './actions' 文件中导出 push 函数
export { push } from './actions';
// 从 './create' 文件中导出 createReduxHistoryContext 函数
export { createReduxHistoryContext } from './create';
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');
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
};
}
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);
}
}
}
}
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;
}
}
}
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>
)
}