Conversion from React Router V2.x to V4.x - reactjs

After updating React Router V2.x on V4.x, all routes go to the main page.
Here is the route on V2.x:
import {Router, Route, hashHistory } from 'react-router'
const routes = <Route component={App}>
<Route path="/results" component={Results} />
<Route path="/" component={Voting} />
</Route>;
ReactDOM.render(
<Router history={hashHistory}>{routes}</Router>,
document.getElementById('app')
);
And these are routes on V4.x:
index.jsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import App from './components/App';
import Voting from './components/Voting';
import Results from './components/Results';
const withAppLayout = Component => props => <App><Component {...props} /></App>
const routes = <Switch>
<Route exact path="/" component={withAppLayout(Voting)} />
<Route path="/results" component={withAppLayout(Results)} />
</Switch>;
ReactDOM.render(
<Router component={App}>
{routes}
</Router>,
document.getElementById('app')
);
App.jsx File:
import React from 'react';
import {List} from 'immutable';
const pair = List.of('Trainspotting', '28 Days Later');
export default React.createClass({
render: function () {
console.log(this.props.children);
return React.cloneElement(this.props.children, {pair: pair});
}
});
Voting.jsx File:
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Winner from './Winner';
import Vote from './Vote';
export default React.createClass({
mixins: [PureRenderMixin],
render: function () {
return <div>
{this.props.winner ?
<Winner ref="winner" winner={this.props.winner} /> :
<Vote {...this.props} />
}
</div>;
}
});
Results.jsx File:
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
export default React.createClass({
mixins: [PureRenderMixin],
render: function () {
return <div>Hello from results!</div>
}
});
How to fix this error?

One thing would be to use :
<Route exact path="/" component={withAppLayout(Voting)} />
The exact will make sure that only that exact path is going to match. But if you have the path="/" after the others, the others should match first though. Is this the real code you're using ?
Edit: See a complete version below.
import {BrowserRouter, Route, Switch} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Voting} />
<Route path="/results" component={Results} />
</Switch>
</BrowserRouter>,
document.getElementById('app')
);
Then, if that works out, you can move the Switch part into an App Component. Also, if you want to use hash history, you need to use a HashRouter that basically initializes a Router with a hash history.

Related

Why i have an error when rendering components outside router? [duplicate]

I have a navbar that is rendered in every route while the route changes on click.
./components/navbar.jsx
import React, { Component } from 'react';
import '../App.css';
import { Link } from 'react-router-dom';
class Navbar extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div id = 'navbar'>
<div className='name-head'>
My Name
</div>
<div id = 'nav-links-container'>
<Link to='/experiences'>
<div className = 'nav-links'>
Experiences
</div>
</Link>
<div className = 'nav-links'>
Projects
</div>
<div className = 'nav-links'>
Skills
</div>
<div className = 'nav-links'>
Resume
</div>
</div>
</div>
);
}
}
export default Navbar;
./components/experiences.jsx
import React, { Component } from 'react';
class Experiences extends Component {
render() {
return (
<div>
<h1>hi</h1>
</div>
);
}
}
export default Experiences;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Experiences from './components/experience';
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom';
ReactDOM.render(
<React.StrictMode>
<Navbar />
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Routes>
</Router>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
The error doesn't come when I remove the <Link> from the experiences tag in navbar.
There is a similar question posted here: Error: useHref() may be used only in the context of a <Router> component
but doesn't help.
I'm using react router v6
Issue
You are rendering the navbar outside the routing context. The Router isn't aware of what routes the links are attempting to link to that it is managing. The reason routing works when directly navigating to "/experiences" is because the Router is aware of the URL when the app mounts.
<Navbar /> // <-- outside router!!
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Routes>
</Router>
Solution
Move it inside the routing context so the Router is aware and can manage routing correctly.
<Router>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Routes>
</Router>
react-router-dom#6.4 Data APIs
If you are using the new Data routers you can hit this issue if you attempt to render a header/navbar outside the RouterProvider component. For this you can create a layout route that is part of the routing configuration passed to createBrowserRouter (and other variants).
Example:
const AppLayout = () => (
<>
<Navbar />
<Outlet />
</>
);
const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<AppLayout />}>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Route>
)
);
...
<RouterProvider router={router} />
in React Route v6 you can solve this giving the route context to your entire App with <BrowserRouter>
This is an complete example of index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { App } from './components/App/App.jsx';
ReactDOM.render(
<React.StrictMode>
<BrowserRouter> //that is the key
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
If you are still having problem with this one, it is because react-router-dom relies on React context to work when you try to unit-test it. This makes <Link /> or <Route /> obsolete.
Try reading the react-router-dom documentation.
Instead of using
render(<Example />)
that have either or inside it, you can try
render(<MemoryRouter>
<Example />
</MemoryRouter>)
Hope this solves your problem
In react-router-dom:6.x and react:18.x, we should use the Router in the following way to resolve the issue:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { App } from './App.js';
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Router> // Router
<App /> // Application
</Router>
</React.StrictMode>
);
Links are inside Navbar &
Navbar is outside of Router
=> links are outside of Router => Router will not manage Links
Solution
Move the Navbar into Router section. Example:
<Router>
<Navbar /> // <===========
<Routes>
<Route />
<Route />
</Routes>
</Router>
Wrap navbar with BrowserRouter
import { BrowserRouter, Routes, Route } from "react-router-dom";
<BrowserRouter>
<AppNavBar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/wall" element={<WallPost />} />
</Routes>
</BrowserRouter>
Install in your project React Router v6.
npm install react-router-dom#6
Then use BrowserRouter in your index.js file, below like this:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
React Router v6 this problem common one, you can simply replace "index.js" code. you will got solution-
import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
This error happens because Link component needs to reach out to react-router context object. Your Navbar component is using Link component but Navbar is not wrapped by router context.
A similar error happens in a testing environment. If you have a component that uses Link component and if you render this component in a testing environment, you will get the same error because Link component needs to access a router context. In the case of test environment:
import { render } from "#testing-library/react";
// this will throw same error
test("testing", () => {
render(<ComponentRendersLink/>);
});
Solution in this case to wrap it with a router
// there are more router options
import { MemoryRouter } from "react-router-dom";
render(
<MemoryRouter>
<ComponentRendersLink />
</MemoryRouter>
);
This is very much an edge case, but in my case it turned out a lib I develop had react-router-dom: 6.0.2 installed and project that uses it v6.3.0
Your links just needs to be within a BrowserRouter component since you use v6
After importing
<BrowserRouter>
<Link to='page'>
</BrowserRouter>

Error: useHref() may be used only in the context of a <Router> component. It works when I directly put the url as localhost:3000/experiences

I have a navbar that is rendered in every route while the route changes on click.
./components/navbar.jsx
import React, { Component } from 'react';
import '../App.css';
import { Link } from 'react-router-dom';
class Navbar extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div id = 'navbar'>
<div className='name-head'>
My Name
</div>
<div id = 'nav-links-container'>
<Link to='/experiences'>
<div className = 'nav-links'>
Experiences
</div>
</Link>
<div className = 'nav-links'>
Projects
</div>
<div className = 'nav-links'>
Skills
</div>
<div className = 'nav-links'>
Resume
</div>
</div>
</div>
);
}
}
export default Navbar;
./components/experiences.jsx
import React, { Component } from 'react';
class Experiences extends Component {
render() {
return (
<div>
<h1>hi</h1>
</div>
);
}
}
export default Experiences;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Experiences from './components/experience';
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom';
ReactDOM.render(
<React.StrictMode>
<Navbar />
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Routes>
</Router>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
The error doesn't come when I remove the <Link> from the experiences tag in navbar.
There is a similar question posted here: Error: useHref() may be used only in the context of a <Router> component
but doesn't help.
I'm using react router v6
Issue
You are rendering the navbar outside the routing context. The Router isn't aware of what routes the links are attempting to link to that it is managing. The reason routing works when directly navigating to "/experiences" is because the Router is aware of the URL when the app mounts.
<Navbar /> // <-- outside router!!
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Routes>
</Router>
Solution
Move it inside the routing context so the Router is aware and can manage routing correctly.
<Router>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Routes>
</Router>
react-router-dom#6.4 Data APIs
If you are using the new Data routers you can hit this issue if you attempt to render a header/navbar outside the RouterProvider component. For this you can create a layout route that is part of the routing configuration passed to createBrowserRouter (and other variants).
Example:
const AppLayout = () => (
<>
<Navbar />
<Outlet />
</>
);
const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<AppLayout />}>
<Route path="/" element={<Home />} />
<Route path="/experiences" element={<Experiences />} />
</Route>
)
);
...
<RouterProvider router={router} />
in React Route v6 you can solve this giving the route context to your entire App with <BrowserRouter>
This is an complete example of index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { App } from './components/App/App.jsx';
ReactDOM.render(
<React.StrictMode>
<BrowserRouter> //that is the key
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
If you are still having problem with this one, it is because react-router-dom relies on React context to work when you try to unit-test it. This makes <Link /> or <Route /> obsolete.
Try reading the react-router-dom documentation.
Instead of using
render(<Example />)
that have either or inside it, you can try
render(<MemoryRouter>
<Example />
</MemoryRouter>)
Hope this solves your problem
In react-router-dom:6.x and react:18.x, we should use the Router in the following way to resolve the issue:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { App } from './App.js';
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Router> // Router
<App /> // Application
</Router>
</React.StrictMode>
);
Links are inside Navbar &
Navbar is outside of Router
=> links are outside of Router => Router will not manage Links
Solution
Move the Navbar into Router section. Example:
<Router>
<Navbar /> // <===========
<Routes>
<Route />
<Route />
</Routes>
</Router>
Wrap navbar with BrowserRouter
import { BrowserRouter, Routes, Route } from "react-router-dom";
<BrowserRouter>
<AppNavBar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/wall" element={<WallPost />} />
</Routes>
</BrowserRouter>
Install in your project React Router v6.
npm install react-router-dom#6
Then use BrowserRouter in your index.js file, below like this:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
React Router v6 this problem common one, you can simply replace "index.js" code. you will got solution-
import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
This error happens because Link component needs to reach out to react-router context object. Your Navbar component is using Link component but Navbar is not wrapped by router context.
A similar error happens in a testing environment. If you have a component that uses Link component and if you render this component in a testing environment, you will get the same error because Link component needs to access a router context. In the case of test environment:
import { render } from "#testing-library/react";
// this will throw same error
test("testing", () => {
render(<ComponentRendersLink/>);
});
Solution in this case to wrap it with a router
// there are more router options
import { MemoryRouter } from "react-router-dom";
render(
<MemoryRouter>
<ComponentRendersLink />
</MemoryRouter>
);
This is very much an edge case, but in my case it turned out a lib I develop had react-router-dom: 6.0.2 installed and project that uses it v6.3.0
Your links just needs to be within a BrowserRouter component since you use v6
After importing
<BrowserRouter>
<Link to='page'>
</BrowserRouter>

One router work but other router not work

Hey i have a reactjs app with this as app.tsx
import tw from 'twin.macro';
import { hot } from 'react-hot-loader/root';
import { Route, BrowserRouter as Router, Routes as Switch } from 'react-router-dom';
import Main from './Main';
import Test from './Test';
const App = () => {
return (
<><Main /><div css={tw`mx-auto w-auto`}>
<Router>
<Switch>
<Route path="/element" element={<Test />} />
<Route path="/" element={<Main />} />
</Switch>
</Router>
</div></>
);
};
export default hot(App);
But when i run a ./node_modules/.bin/webpack --watch --progress the localhost/ work but localhost/element not work i have a 404 error
Not Found The requested URL was not found on this server
Can you tell me why its not work?
(i use 6.2.1 of react-router-dom)
first and foremost you need to mention the version you are using , and try this if it works for u :-
your components should look like this, to work:-
import tw from 'twin.macro';
import { hot } from 'react-hot-loader/root';
import { Route, Routes} from 'react-router-dom';
import Main from './Main';
import Test from './Test';
const App = () => {
return (
<><Main /><div css={tw`mx-auto w-auto`}>
<Routes>
<Route exact path="/element" element= {<Test />} />
<Route exact path="/" element= {<Main/>} />
</Routes>
</div></>
);
};
NB: try to wrap your index.tsx component with the <BrowserRouter> </BrowserRouter> like this :
import { BrowserRouter } from "react-router-dom";
ReactDOM.render( <BrowserRouter> <App/> </BrowserRouter>, document.getElementById('root'))
therefore you can remove the BrowserRouter from the app.tsx
I think you're using ‘react-router-dom’ newer version. Switch is no longer in the new version. please read here
Switch' is not exported from 'react-router-dom'
import { Route, BrowserRouter as Router, Routes as Switch } from 'react-router-dom';
function App() {
const Main = () => {
return (
<h1>Main</h1>
)
}
const Test = <h1>Test</h1>
return (
<div >
<Router>
<Switch>
<Route path="/element" element={<Main />} />
<Route path="/" element={Test} />
</Switch>
</Router>
</div>
);
}
export default App;
You need to use the <Main /> structure

react-router-dom usage in index.js

In my index.js file:
import React from 'react';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom';
import App from './App';
import Contact from './Contact';
import ReactDOM from 'react-dom';
//import RouterMapping from './RouterMapping';
const RouterMapping = () => (
<Router>
<Route exact path='/' component={App} />
<Route path='/contact' component={Contact} />
</Router>
);
ReactDOM.render(
<RouterMapping />,
document.getElementById('root')
);
When rendering the page /, it displays nothing (should go to App component per my understanding. The App component itself is running OK if I replace:
ReactDOM.render(
<RouterMapping />,
document.getElementById('root')
);
to
ReactDOM.render(
<App />,
document.getElementById('root')
);
Did I do something fundamentally wrong? Newbie to React...
Update on Mar 16
I think I figured out how to make the router work.
I have refactored my RouterMapping to RoutingConfig as below:
import React from 'react';
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom';
import Home from './Home';
import Contact from './Contact';
const RoutingConfig = () => (
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route path="/contact" component={Contact}/>
</div>
</Router>
);
export default RoutingConfig;
Nothing peculiar.
Now, instead of rendering RoutingConfig in my index.js, I still render the App component in my index.js:
ReactDOM.render(
<App />,
document.getElementById('root')
);
The routing mapping is done now in App.js:
class App extends Component {
render() {
return (
<RoutingConfig />
);
}
}
Now it works. Please see attached screenshot:
My explanation
I think the key here is to demote the RoutingConfig to App.js. From index.js render App.js, App.js will act as the entry point to my whole app and determine which component (Home, Contact) to load based on routing.
Above for your information and hope it is helpful.

routing in react-redux application doesn't working

Routing doesn't working. Each route always renders component MainPage
index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import reducers from './reducers'
import App from './components/App'
let store = createStore(reducers);
render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root')
);
component App with described routes
import React from 'react'
import { browserHistory, Router, Route } from 'react-router'
import MainPage from '../containers/MainPage';
import FavoritesPage from '../containers/FavoritesPage';
import Error404 from '../containers/Error404';
export default () => (
<Router history={browserHistory}>
<Route path="/" component={MainPage}>
<Route path="/favorites" component={FavoritesPage}/>
<Route path="*" component={Error404}/>
</Route>
</Router>
);
Do you see any problems here?
Your routes are nested which means that child routes will render in the MainPage component. Sometimes this is desired and you only have to render the children somewhere:
class MainPage extends Component {
render() {
return (
<div> { this.props.children }</div>
)
}
}
If you don't want this move the inner routes outside:
<Router history={browserHistory}>
<Route path="/" component={MainPage} />
<Route path="/favorites" component={FavoritesPage}/>
<Route path="*" component={Error404}/>
</Router>

Resources