React Router 无Hook编程式导航方案求助(ReactTS项目)
React Router 无Hook编程式导航方案(组件逻辑迁移至服务层)
我有一个基于ReactTS的Web应用,使用react-router库实现路由导航,需要在点击“Registration”按钮时编程式跳转到注册页面。
我了解到react-router中两种编程式导航方式:
- 使用
useNavigateHook —— 仅能在组件内部使用 - 使用
redirect函数 —— 仅适用于loader
我使用带Hook的函数组件,但不想在组件内保留过多逻辑,希望将逻辑迁移至服务层,正在寻找无需Hook的编程式导航方案。
我的代码如下:
import { redirect, useNavigate } from 'react-router-dom'; import { AUTHORIZATION_REGISTRATION_MAILBOX } from '../../../constants/routes'; export const LogInComponent = () => { const navigate = useNavigate(); const goToRegistration = () => { redirect(AUTHORIZATION_REGISTRATION_MAILBOX); // 此方法无效 navigate(AUTHORIZATION_REGISTRATION_MAILBOX); // 此方法有效但仅能在组件内使用 } return ( <div> <button className='enter-button' onClick={goToRegistration}> Registration </button> </div> ); };
更新1:期望的解决方案结构
我希望实现的代码结构分为三层:
- 组件调用业务服务
import { registrateUser } from './registrationService' export const RegistrationComponent = () => { const registrate = () => { registrateUser(); } return ( <div> <button onClick={registrate}> Registrate </button> </div> ); }
- 业务服务执行业务逻辑后调用导航服务跳转
import { redirectTo } from './navigationService' export const registrateUser = () => { // 执行业务逻辑 redirectTo(/* 目标路径 */); }
- 导航服务完成跳转
export const redirectTo = (path: string) => { window.location.href = path; // 示例方法 }
使用window.location.href的方案可以实现跳转,但会清空上下文和状态管理中的值,并非理想方案。
可行解决方案
方案一:通过React上下文传递导航能力
这种方案符合React Hook规则,能在服务层安全使用导航功能,同时保留应用状态。
- 创建导航上下文
// src/contexts/NavigationContext.tsx import { createContext, useContext, useNavigate, NavigateFunction } from 'react-router-dom'; const NavigationContext = createContext<NavigateFunction | undefined>(undefined); export const NavigationProvider = ({ children }: { children: React.ReactNode }) => { const navigate = useNavigate(); return ( <NavigationContext.Provider value={navigate}> {children} </NavigationContext.Provider> ); }; export const useNavigation = () => { const context = useContext(NavigationContext); if (!context) { throw new Error('useNavigation必须在NavigationProvider内部使用'); } return context; };
- 在根路由组件中包裹导航上下文
确保NavigationProvider在Router内部,才能正确获取useNavigate实例:
// src/App.tsx import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { NavigationProvider } from './contexts/NavigationContext'; import { LogInComponent } from './components/LogInComponent'; import { RegistrationComponent } from './components/RegistrationComponent'; function App() { return ( <Router> <NavigationProvider> <Routes> <Route path="/login" element={<LogInComponent />} /> <Route path="/register" element={<RegistrationComponent />} /> </Routes> </NavigationProvider> </Router> ); } export default App;
- 封装导航服务
// src/services/navigationService.ts import { useNavigation } from '../contexts/NavigationContext'; export const useNavigationService = () => { const navigate = useNavigation(); const redirectTo = (path: string) => { navigate(path); }; const redirectToWithReplace = (path: string) => { navigate(path, { replace: true }); }; return { redirectTo, redirectToWithReplace }; };
- 改造业务服务,集成导航能力
用自定义Hook封装业务逻辑,确保能访问导航上下文:
// src/services/registrationService.ts import { useNavigationService } from './navigationService'; import { AUTHORIZATION_REGISTRATION_MAILBOX } from '../constants/routes'; export const useRegistrationService = () => { const { redirectTo } = useNavigationService(); const registrateUser = async () => { // 执行你的业务逻辑,比如用户注册API请求 // await api.post('/register', userData); // 跳转至目标页面 redirectTo(AUTHORIZATION_REGISTRATION_MAILBOX); }; return { registrateUser }; };
- 组件中使用业务服务Hook
// src/components/RegistrationComponent.tsx import { useRegistrationService } from '../services/registrationService'; export const RegistrationComponent = () => { const { registrateUser } = useRegistrationService(); const registrate = () => { registrateUser(); }; return ( <div> <button onClick={registrate}> Registrate </button> </div> ); };
方案二:直接使用React Router导航器实例
如果项目使用createBrowserRouter创建路由,可以直接导出导航器实例,在服务层直接调用。
- 创建路由并导出导航器
// src/router.ts import { createBrowserRouter, RouterProvider, NavigateFunction } from 'react-router-dom'; import { LogInComponent } from './components/LogInComponent'; import { RegistrationComponent } from './components/RegistrationComponent'; export const router = createBrowserRouter([ { path: '/login', element: <LogInComponent />, }, { path: '/register', element: <RegistrationComponent />, }, ]); // 声明导航变量 export let navigate: NavigateFunction; // 路由初始化后获取导航实例 router.initialize().then(instance => { navigate = instance.navigate; });
- 改造导航服务
// src/services/navigationService.ts import { navigate } from '../router'; export const redirectTo = (path: string) => { if (navigate) { navigate(path); } else { // 兜底方案:导航器未初始化时使用window.location window.location.href = path; } };
- 业务服务直接调用导航服务
// src/services/registrationService.ts import { redirectTo } from './navigationService'; import { AUTHORIZATION_REGISTRATION_MAILBOX } from '../constants/routes'; export const registrateUser = async () => { // 执行业务逻辑 redirectTo(AUTHORIZATION_REGISTRATION_MAILBOX); };
- 组件调用业务服务
// src/components/RegistrationComponent.tsx import { registrateUser } from './registrationService' export const RegistrationComponent = () => { const registrate = () => { registrateUser(); } return ( <div> <button onClick={registrate}> Registrate </button> </div> ); }
内容的提问来源于stack exchange,提问作者Alexander Plekhanov
相关产品推荐
相关产品推荐

