'How can I resolver " useHref() may be used only in the context of a <Router> component" when component is already inside <Router>?
I am currently writing Jest tests for my react app.
When I run the tests for <Container />, the error message shows:
" useHref() may be used only in the context of a component."
However, <Container /> is already inside <Router/>.
I have read other similar questions on here, and the answers all say to put the component inside the <Router/>, which is what I did.
Container.test.js
import { render, unmountComponentAtNode } from 'react-dom';
import Container from '../components/Container.tsx';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import { configure, shallow, mount } from 'enzyme';
import { Provider, useDispatch } from 'react-redux';
import { act } from "react-dom/test-utils";
import configureStore from 'redux-mock-store';
configure({adapter: new Adapter()});
const initState = { allCard: [], expansions: [] };
const mockStore = configureStore();
const wrapper = mount(<Provider store={mockStore(initState)}><Container/></Provider>);
describe('Container', () => {
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
it('renders itself and its children without crashing', () => {
render(<Provider store={mockStore(initState)}><Container /></Provider>, container);
});
it('renders itself without crashing', () => {
const tree = wrapper.render();
expect(tree.toJSON).toMatchSnapshot();
});
});
App.js
import './App.css';
import Container from './components/Container';
import About from './components/RouteComponents/About';
import NavBar from './components/NavBar';
import Tutorials from './components/RouteComponents/Tutorials';
import Suggest from './components/RouteComponents/Suggest';
import Report from './components/RouteComponents/Report';
import { Route, Routes, BrowserRouter as Router } from 'react-router-dom';
import GlobalStyles from './components/styled/Globals.styled';
import { useState, createContext } from 'react';
function App() {
const [ darkMode, setDarkMode ] = useState(false);
return (
<Router>
{darkMode? <GlobalStyles/> : null}
<div className="App">
<NavBar darkMode={darkMode} setDarkMode={setDarkMode}/>
<Routes>
<Route path='/' element= {<UserContext.Provider value={darkMode}>
<Container />
</UserContext.Provider>} />
<Route path='/about' element={<About />} />
<Route path='/tutorial' element= {<Tutorials />} />
<Route path='/suggest' element={<Suggest />} />
<Route path='/report' element= {<Report />} />
</Routes>
</div>
</Router>
);
}
export const UserContext = createContext(null);
export default App;
Container.tsx
import { useEffect, ReactElement } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { useDispatch } from 'react-redux'
import ErrorBoundary from './ErrorBoundary';
import ExpansionView from './ExpansionView';
import DisplayView from './DisplayView';
import SearchCard from './SearchCard';
import MurlocTidehunter from '../image/murlocTidehunter.png';
import SelectExpanionsMurloc from '../image/selectExpansions.png';
import { StyledContainer } from './styled/Container.styled';
import { eliminateMurloc } from '../utils/eliminateMurloc';
// Component - parent component that fetches allCard once when app launches
const Container = (): ReactElement => {
const dispatch = useDispatch();
useEffect(() => {
const options: object = {
method: 'GET',
url: 'https://omgvamp-hearthstone-v1.p.rapidapi.com/cards',
params: {collectible: '1'},
headers: {
'x-rapidapi-host': 'omgvamp-hearthstone-v1.p.rapidapi.com',
'x-rapidapi-key': 'xxxxxxxxxx'
}
};
// function to fetch all collectible cards from hearthstone api
const fetchAllCards = async (): Promise<any> => {
try {
const response = await axios.request(options);
dispatch({type: 'SET_ALL_CARD', payload: response.data})
} catch(e) {
console.log(e)
}
}
fetchAllCards();
}, []);
return (
<StyledContainer>
<h1 id='hearthfin'>Hearthfin</h1>
<img id='tideHunter' className='deletableMurloc' src={MurlocTidehunter} alt="Hearthstone Murloc Analisis Videojuegos Zehngames - [email protected]" />
<img id='selectExpMurloc' className='deletableMurloc' onClick={() => eliminateMurloc('selectExpMurloc')} src={SelectExpanionsMurloc} alt="Murloc that say select expansions first!" />
<ErrorBoundary>
<ExpansionView />
</ErrorBoundary>
<ErrorBoundary>
<DisplayView />
</ErrorBoundary>
<p id='missingCard'>See a missing AOE card? Make a suggestion <Link to='suggest'>here!</Link></p>
<ErrorBoundary>
<SearchCard />
</ErrorBoundary>
<br/>
</StyledContainer>
);
}
export default Container;
Solution 1:[1]
You are rendering the navbar outside the routing context. The Router isn't aware of what routes the links are attempting to link t that it is managing. The reason routing works when directly navigating to "/" is because the Router is aware of the URL when the app mounts
Solution 2:[2]
In your "Container.tsx" file, wrap the component like this:
import { BrowserRouter as Router } from 'react-router-dom';
<Router>
<StyledContainer>
//jsx elements
</StyledContainer>
<Router>
Now "Link" of the Container component will not get confused as Link will find Router as a wrapper of its component.
Don't forget to remove from the "app.js" file. The rest will be the same as before.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | MouadAmzil |
| Solution 2 | Hasan Sharif |
