Reputation: 1061
See my code below. I'm trying to add this button that goes back to the previous page using react-router-dom
but I get the below error, and also all the components on my website disappear.
Error:
useNavigate() may be used only in the context of a component
My code:
function App() {
const navigate = useNavigate();
return (
<BrowserRouter>
<div>
<button onClick={() => navigate(-1)}>go back</button>
<Nav/>
<Routes>
<Route exact path="/" element={<Home/>}/>
<Route exact path="/home" element={<Home/>}/>
<Route exact path="/upcoming/:user" element={<Upcoming/>}/>
<Route exact path="/record/:user" element={<Record/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
</div>
</BrowserRouter>
);
}
And this is the error I got in console:
Upvotes: 91
Views: 244488
Reputation: 39
You can also make a mock tag and wrap the tag you want to test inside a
<BroserRouter></BroserRouter>
tag like this:
const Mocktest =()=>{
return(
<BrowserRouter>
<App/>
</BrowserRouter>
)
}
and then render the Mocktest
component
like this:
test('test app',()=>{
render(<Mocktest/>);
const yourTag= screen.findByLabelText(/choose time/i);
expect(yourTag).toBeInTheDocument();
});
Upvotes: 1
Reputation: 31
In the unit tests wrap your components with MemoryRouter and provide the initialEntries correctly with pathname and search params
import { MemoryRouter, Routes, Route } from 'react-router-dom'
import { renderWithProviders, screen } from '../../../test/test-utils'
import { Case } from '../index'
import { Home } from '../../home/index'
describe('Case component', () => {
const queryParams = 'caseRef=2023030026&token=testToken&refreshToken=testRefreshToken'
it('renders "Loading..." when token, refreshToken, or caseRef are missing', () => {
renderWithProviders(
<MemoryRouter initialEntries={[{ pathname: '/case?', search: queryParams }]}>
<Case />
</MemoryRouter>
)
expect(screen.getByText(/Loading.../)).toBeInTheDocument()
})
it('renders Home once the application has loaded', () => {
renderWithProviders(
<MemoryRouter initialEntries={['/case', '/2023030026']}>
<Routes>
<Route path="/:caseRef" element={<Home />} />
</Routes>
</MemoryRouter>
)
expect(screen.getByText(/Overview/)).toBeInTheDocument()
})
})
Upvotes: 2
Reputation: 412
For future reference: This error may also occur
<Router>
from react-router-dom
and import useNavigate
from react-router
useNavigate
et al from a different version of the package than BrowserRouter
, e.g. v5 and v6Upvotes: 31
Reputation: 102207
This error throws in useNavigate. useInRouterContext will check if the component(which uses useNavigate
hook) is a descendant of a <Router>
. If the component is not a descendant of the <Router>
component, it will throw this error.
Here's the source code to explain why you can't use useNavigate
, useLocation
outside of the Router
component:
useNavigate
usesuseLocation
underly,useLocation
will get the location from LocationContext provider. If you want to get the react context, you should render the component as the descendant of a context provider.Router
component use the LocationContext.Provider and NavigationContext.Provider. That's why you need to render the component as the children, so thatuseNavigate
hook can get the context data fromNavigationContext
andLocationContext
providers.
Your environment is browser, so you need to use BrowserRouter
. BrowserRouter
is built based on Router
.
Refactor to this:
App.jsx
:
function App() {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate(-1)}>go back</button>
<Nav/>
<Routes>
<Route exact path="/" element={<Home/>}/>
<Route exact path="/home" element={<Home/>}/>
<Route exact path="/upcoming/:user" element={<Upcoming/>}/>
<Route exact path="/record/:user" element={<Record/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
</div>
);
}
index.jsx
:
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
)
Upvotes: 118
Reputation: 533
The component where you use useNavigate
should wrap by <Router> ... </Router>
e.g.
<Router>
<AuthProvider> <----- useNavigate used in this component
<NavBar />
<Routes>
<Route exact path="/login" element={<LoginForm />} />
<Route exact path="/register" element={<RegisterForm />} />
</Routes>
</AuthProvider>
</Router>
<OtherComponent/> <----- if useNavigate use inside this component then
error occur because useNavaigate work inside
<Router> Component so it should wrap inside <Router>
Like this :
<Router>
<AuthProvider>
<NavBar />
<Routes>
<Route exact path="/login" element={<LoginForm />} />
<Route exact path="/register" element={<RegisterForm />} />
</Routes>
</AuthProvider>
<OtherComponent/>
</Router>
Upvotes: 2
Reputation: 6345
My exact error was
console.error
Error: Uncaught [Error: useNavigate() may be used only in the context of a <Router> component.]
and it happened only when running the following unit test code:
// Act
const button: HTMLElement = render(<MyButton myChoice="myChoice" />).container;
// Assert
expect(button).toHaveTextContent("myChoice");
I solved the error by wrapping the rendered element with <BrowserRouter>
:
// Act
const button: HTMLElement = render(
<BrowserRouter>
<MPBenefitChoiceButton selectedChoice={selectedChoice} />
</BrowserRouter>
).container;
// Assert
expect(button).toHaveTextContent(buttonText);
Upvotes: 6
Reputation: 10071
You are using hook outside BrowserRouter
provider. That's why you are getting errors. Refactor your component like below to solve this issue:
function Root() {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate(-1)}>go back</button>
<Nav />
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/home" element={<Home />} />
<Route exact path="/upcoming/:user" element={<Upcoming />} />
<Route exact path="/record/:user" element={<Record />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
);
}
function App() {
return (
<BrowserRouter>
<Root />
</BrowserRouter>
);
}
Upvotes: 18
Reputation: 311
I had this same issue but with testing a component that has the hook useNavigate
on it. Solved it by wrapping my component in a Route
with Routes
and BrowserRouter
. Hope this helps others with the same error.
let container = null;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
test('finds qualified insurrance policies', () => {
act(() => {
render(
<BrowserRouter>
<Routes>
<Route path="*" element= {<QuaifiedPolicies policyTypes={false} />}/>
</Routes>
</BrowserRouter>
, container);
});
expect(screen.getByLabelText('some text'))
});
Upvotes: 31