React-Router和Redux的Hooks

本文介绍了如何在React应用中利用React-Redux提供的useSelector和useDispatch hooks管理Redux状态,以及如何通过react-router-dom中的useHistory, useLocation, useParams和useRouteMatch hooks处理路由操作。

react-redux-hook

  • react-redux支持了hook的解决方案,提供两个方法,很方便我们在项目去使用
  • useSelector 通过传入 selector 函数,你就可以从从 Redux 的 store 中获取 状态(state) 数据。
  • useDispatch 通过 dispatch 派发一个方法,执行reducer中的方法。
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'

const MyRedux = () => {
  const { username, password } = useSelector(state => Object.assign({}, state.userInfo))
  const dispatch = useDispatch()

  return (
    <div>
      <h3>{username}</h3>
      <hr />
      <button onClick={
        e => {
          dispatch({
            type: 'setname',
            name: 'aaaaa'
          })
        }
      }>修改一下姓名</button>
    </div>
  )
}

react-router-dom-hook

  • useHistory 该钩子提供对 history 对象的访问。
  • useLocation 该挂钩提供对 location 对象的访问
  • useParams 返回URL参数的键/值对的对象。
  • useRouteMatch 当您只需要路由匹配数据而无需实际渲染路由时,可以使用此钩子。

参考案例:React-Router Hooks

<think> 我们参考用户提供的引用内容,并结合React+TypeScript项目中常见的目录结构,来回答如何配置`react-redux``react-router-dom`的目录。 ### 1. 安装依赖 首先,确保已经安装了必要的依赖包。根据引用[2][3][5],我们需要以下依赖: - `react-router-dom`:用于路由管理(引用[1][3][4]) - `redux`、`react-redux`、`@reduxjs/toolkit`:用于状态管理(引用[2][5]) - 可选:`connected-react-router`或`react-router-redux`(旧版,引用[1][3])用于将路由状态同步到Redux store,但注意在React Router v6中可能有变化。引用[5]使用了最新的`reduxjs/toolkit``redux-persist`,并没有使用`connected-react-router`,因此我们可以采用更现代的方式。 安装命令(使用npm或yarn): ```bash # 核心依赖 npm install react-router-dom @types/react-router-dom npm install @reduxjs/toolkit react-redux # 如果需要持久化存储 npm install redux-persist # 如果使用connected-react-router(适用于React Router v5及以下,注意v6不兼容) # 但根据引用[3]提到的是旧版,所以在新项目中不推荐 ``` ### 2. 目录结构配置 在React+TypeScript项目中,推荐将路由状态管理的代码分别放在特定目录中。以下是一个常见的目录结构: #### 项目目录示例 ``` src/ ├── store/ # Redux状态管理 │ ├── index.ts # 创建store实例 │ ├── rootReducer.ts # 根reducer(可能包含路由reducer) │ ├── slices/ # Redux Toolkit的slice文件 │ │ ├── authSlice.ts │ │ └── ... │ └── hooks.ts # 自定义hooks,如useAppDispatch, useAppSelector ├── router/ # 路由配置 │ ├── index.tsx # 路由定义 │ ├── routes.ts # 路由配置数组(可选) │ └── PrivateRoute.tsx # 权限路由组件(可选) ├── components/ # 通用组件 ├── pages/ # 页面组件 ├── App.tsx # 主应用组件,包含路由布局 └── index.tsx # 入口文件 ``` ### 3. 配置Redux Store(含路由同步) #### 3.1 创建Store(不使用connected-react-router) 在React Router v6中,通常不再需要将路由状态同步到Redux store(引用[5])。因此,我们按照常规方式创建store: **文件:`src/store/index.ts`** ```typescript import { configureStore } from '@reduxjs/toolkit'; import rootReducer from './rootReducer'; // 创建store export const store = configureStore({ reducer: rootReducer, }); // 导出类型以便在组件中使用 export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; ``` **文件:`src/store/rootReducer.ts`** ```typescript import { combineReducers } from '@reduxjs/toolkit'; import authReducer from './slices/authSlice'; // 示例slice const rootReducer = combineReducers({ auth: authReducer, // 其他reducer... }); export default rootReducer; ``` #### 3.2 配置路由 **文件:`src/router/index.tsx`** ```typescript import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import HomePage from '../pages/Home'; import AboutPage from '../pages/About'; import LoginPage from '../pages/Login'; // 定义路由 const router = createBrowserRouter([ { path: '/', element: <HomePage />, }, { path: '/about', element: <AboutPage />, }, { path: '/login', element: <LoginPage />, }, ]); // 导出路由提供者组件(用于在App.tsx中使用) export default function Router() { return <RouterProvider router={router} />; } ``` ### 4. 在入口文件中集成Redux路由 **文件:`src/index.tsx`** ```typescript import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; import { store } from './store'; import Router from './router'; import './index.css'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <Provider store={store}> <Router /> </Provider> </React.StrictMode> ); ``` ### 5. 配置权限路由(可选) 如果需要实现权限路由(如引用[4]中的`FrontendAuth`),可以创建一个高阶组件: **文件:`src/router/PrivateRoute.tsx`** ```typescript import { Navigate, useLocation } from 'react-router-dom'; import { useAppSelector } from '../store/hooks'; import { selectIsAuthenticated } from '../store/slices/authSlice'; export default function PrivateRoute({ children }: { children: JSX.Element }) { const isAuthenticated = useAppSelector(selectIsAuthenticated); const location = useLocation(); if (!isAuthenticated) { // 重定向到登录页,并记录当前路径以便登录后跳转 return <Navigate to="/login" state={{ from: location }} replace />; } return children; } ``` 然后在路由配置中使用: ```typescript // 修改src/router/index.tsx中的路由配置 const router = createBrowserRouter([ { path: '/', element: <PrivateRoute><HomePage /></PrivateRoute>, }, // ...其他路由 ]); ``` ### 6. 使用Redux状态操作 在组件中,使用`react-redux`提供的hooks来访问状态分发操作: **文件:`src/store/hooks.ts`**(可选,用于添加类型) ```typescript import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; import type { RootState, AppDispatch } from './index'; // 在整个应用中使用,代替普通的`useDispatch``useSelector` export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; ``` 在组件中: ```typescript import { useAppSelector, useAppDispatch } from '../store/hooks'; import { login } from '../store/slices/authSlice'; const LoginPage: React.FC = () => { const dispatch = useAppDispatch(); const isAuthenticated = useAppSelector(state => state.auth.isAuthenticated); const handleLogin = () => { dispatch(login({ username: 'user', password: 'pass' })); }; // ...渲染登录表单 }; ``` ### 7. 持久化存储(可选,参考引用[5]) 如果需要持久化存储部分状态(如用户登录状态),可以使用`redux-persist`: **修改`src/store/index.ts`**: ```typescript import { configureStore } from '@reduxjs/toolkit'; import rootReducer from './rootReducer'; import { persistReducer, persistStore } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; // 默认使用localStorage // 持久化配置 const persistConfig = { key: 'root', storage, whitelist: ['auth'], // 只持久化auth状态 }; const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, }); export const persistor = persistStore(store); ``` **修改入口文件**: ```typescript // src/index.tsx import { PersistGate } from 'redux-persist/integration/react'; import { persistor } from './store'; // 在Provider内部包裹PersistGate root.render( <React.StrictMode> <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <Router /> </PersistGate> </Provider> </React.StrictMode> ); ``` ### 总结 在React+TypeScript项目中配置`react-redux``react-router-dom`的关键步骤包括: 1. **安装依赖**:确保安装正确版本的库。 2. **目录结构**:将路由配置放在`router/`目录,状态管理放在`store/`目录。 3. **创建Redux Store**:使用`@reduxjs/toolkit`简化配置。 4. **路由配置**:使用React Router v6的`createBrowserRouter``RouterProvider`。 5. **集成到入口文件**:用`Provider`包裹整个应用,并添加路由。 6. **可选**:实现权限路由、持久化存储。 这种结构清晰且易于维护[^2][^5]。对于路由状态同步到ReduxReact Router v6不再推荐,因此应避免使用`connected-react-router`(除非使用旧版)[^3]。 §§相关问题§§ 1. React Router v6v5在配置上有什么主要区别? 2. 在React+TypeScript项目中如何优化Redux状态管理? 3. 如何测试集成React RouterReduxReact组件? </think>在 React TypeScript 项目中配置 `react-redux` `react-router-dom` 的标准目录结构如下(基于最佳实践引用内容): ### 目录结构配置方案 ```markdown src/ ├── store/ # Redux 状态管理核心目录 │ ├── index.ts # Store 创建配置入口 │ ├── slices/ # Redux Toolkit 的切片(推荐) │ │ ├── authSlice.ts # 示例:认证状态切片 │ │ └── userSlice.ts │ ├── actions/ # 传统 Redux actions(可选) │ ├── reducers/ # 传统 Redux reducers(可选) │ └── hooks.ts # 自定义 Redux hooks ├── router/ # 路由配置目录 │ ├── index.tsx # 路由配置主文件 │ ├── routes.ts # 路由配置数组 │ ├── PrivateRoute.tsx # 受保护路由组件 │ └── types.ts # 路由类型定义 ├── components/ # 通用组件 ├── pages/ # 页面级组件 ├── App.tsx # 主应用组件 └── index.tsx # 应用入口文件 ``` ### 详细配置步骤 #### 1. Redux 配置 (`store/` 目录) **文件:`src/store/index.ts`** ```typescript import { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import rootReducer from './rootReducer'; const persistConfig = { key: 'root', storage, whitelist: ['auth'] // 持久化的状态切片 }; const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }) }); export const persistor = persistStore(store); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; ``` **文件:`src/store/slices/authSlice.ts`**(使用 Redux Toolkit) ```typescript import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface AuthState { token: string | null; isAuthenticated: boolean; } const initialState: AuthState = { token: null, isAuthenticated: false }; const authSlice = createSlice({ name: 'auth', initialState, reducers: { loginSuccess(state, action: PayloadAction<string>) { state.token = action.payload; state.isAuthenticated = true; }, logout(state) { state.token = null; state.isAuthenticated = false; } } }); export const { loginSuccess, logout } = authSlice.actions; export default authSlice.reducer; ``` #### 2. 路由配置 (`router/` 目录) **文件:`src/router/routes.ts`** ```typescript import { RouteObject } from 'react-router-dom'; import HomePage from '../pages/Home'; import LoginPage from '../pages/Login'; import Dashboard from '../pages/Dashboard'; const routes: RouteObject[] = [ { path: '/', element: <HomePage /> }, { path: '/login', element: <LoginPage /> }, { path: '/dashboard', element: <Dashboard />, // 需要认证的路由可添加 meta 信息 meta: { requiresAuth: true } } ]; export default routes; ``` **文件:`src/router/index.tsx`** ```typescript import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import routes from './routes'; import PrivateRoute from './PrivateRoute'; const processedRoutes = routes.map(route => ({ ...route, element: route.meta?.requiresAuth ? <PrivateRoute>{route.element}</PrivateRoute> : route.element })); const router = createBrowserRouter(processedRoutes); export default function AppRouter() { return <RouterProvider router={router} />; } ``` #### 3. 受保护路由组件 (`PrivateRoute.tsx`) ```typescript import { Navigate, useLocation } from 'react-router-dom'; import { useAppSelector } from '../store/hooks'; import { RootState } from '../store'; export default function PrivateRoute({ children }: { children: JSX.Element }) { const location = useLocation(); const isAuthenticated = useAppSelector( (state: RootState) => state.auth.isAuthenticated ); return isAuthenticated ? ( children ) : ( <Navigate to="/login" state={{ from: location }} replace /> ); } ``` #### 4. 应用入口集成 (`index.tsx`) ```typescript import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import { store, persistor } from './store'; import AppRouter from './router'; import './index.css'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <AppRouter /> </PersistGate> </Provider> </React.StrictMode> ); ``` #### 5. 主应用组件 (`App.tsx`) ```typescript import { Toaster } from 'react-hot-toast'; function App() { return ( <div className="app-container"> {/* 全局通知组件 */} <Toaster position="top-right" /> {/* 其他全局组件/布局 */} </div> ); } export default App; ``` ### 关键配置说明 1. **Redux 与路由集成** - 使用 `@reduxjs/toolkit` 简化 Redux 配置[^2][^5] - 通过 `persistReducer` 实现状态持久化[^5] - 路由守卫通过 `PrivateRoute` 组件实现认证保护[^4] 2. **类型安全** - 使用 TypeScript 类型定义所有状态路由配置 - 导出 `RootState` `AppDispatch` 类型供组件使用 3. **目录分离** - `store/`:集中管理所有状态逻辑 - `router/`:隔离路由配置路由守卫逻辑 - `pages/`:页面组件与路由解耦 4. **最佳实践** - 使用 Redux Toolkit 的 `createSlice` 替代传统 action/reducer[^2] - 通过 `RouterProvider` 配置路由 (React Router v6)[^4] - 自定义 hooks 访问 store (`useAppSelector`)[^5] > 提示:对于大型项目,可添加 `store/selectors.ts` 集中管理状态选择器,优化性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值