React Router useNavigate报错:需在<Router>上下文使用的解决求助
问题原因
你在App组件的顶层直接调用了useNavigate()钩子,但<BrowserRouter>是在App组件的return语句中才渲染的。这意味着当App组件初始化时,<Router>的上下文还不存在,所以useNavigate()无法获取到路由上下文,从而抛出错误。
解决方案
有两种简单的修复方式:
方式一:将首页表单抽为独立组件
把根路径的表单逻辑抽成一个单独的组件,在这个组件内部调用useNavigate(),这样组件就处于<Router>的上下文范围内了:
// Home.jsx import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import axios from 'axios'; export default function Home() { const [destination, setDestination] = useState(''); const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); const data = { destination }; await axios.post('/travel', data); navigate("/travelplan"); }; return ( <div> <form onSubmit={handleSubmit} style={{ textAlign: "center" }}> <div> <label> Destination: <input type="text" value={destination} onChange={(e) => setDestination(e.target.value)} /> </label> </div> <button type="submit">Submit</button> </form> </div> ); }
然后在App组件中引入并使用这个组件:
// App.jsx import React, { useState } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import axios from 'axios'; import NavBar from './NavBar'; import Footer from './Footer'; import SignIn from './SignIn'; import SignUp from './SignUp'; import TravelPlan from './TravelPlan'; import Home from './Home'; // 引入新组件 function App() { const [loggedIn, setLoggedIn] = useState(false); return ( <div> <BrowserRouter> <NavBar loggedIn={loggedIn} setLoggedIn={setLoggedIn} handleSignin={handleSignin} /> <Routes> <Route path="/" element={<Home />} /> {/* 使用Home组件 */} <Route path="/signin" element={<SignIn handleSignin={handleSignin} />} /> <Route path="/signup" element={<SignUp />} /> <Route path="/travelplan" element={<TravelPlan />} /> </Routes> <Footer /> </BrowserRouter> </div > ); } export default App;
方式二:将<BrowserRouter>移到App组件外部
修改index.js(或渲染App的入口文件),让<BrowserRouter>包裹整个App组件,这样App组件就处于路由上下文范围内了:
// index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter> <App /> </BrowserRouter> );
然后修改App组件,移除内部的<BrowserRouter>:
// App.jsx import React, { useState } from 'react'; import { Routes, Route, useNavigate } from 'react-router-dom'; import axios from 'axios'; import NavBar from './NavBar'; import Footer from './Footer'; import SignIn from './SignIn'; import SignUp from './SignUp'; import TravelPlan from './TravelPlan'; function App() { const [loggedIn, setLoggedIn] = useState(false); const navigate = useNavigate(); // 现在可以正常调用了 const [destination, setDestination] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); const data = { destination }; await axios.post('/travel', data); navigate("/travelplan"); }; return ( <div> <NavBar loggedIn={loggedIn} setLoggedIn={setLoggedIn} handleSignin={handleSignin} /> <Routes> <Route path="/" element={ <div> <form onSubmit={handleSubmit} style={{ textAlign: "center" }}> <div> <label> Destination: <input type="text" value={destination} onChange={(e) => setDestination(e.target.value)} /> </label> </div> <button type="submit">Submit</button> </form> </div> }> </Route> <Route path="/signin" element={<SignIn handleSignin={handleSignin} />} /> <Route path="/signup" element={<SignUp />} /> <Route path="/travelplan" element={<TravelPlan />} /> </Routes> <Footer /> </div > ); } export default App;
补充说明
两种方式都能解决问题,推荐第一种方式,因为它更符合组件拆分的原则,把路由相关的逻辑局限在对应的页面组件中,代码结构更清晰。
内容的提问来源于stack exchange,提问作者Makoto Miyazaki
相关产品推荐
相关产品推荐

