react-router switch not working as expected - javascript

I'm learning react and got stuck at react-routes
consider the following:
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import HelloWorld from "./HelloWorld.jsx";
const Root = () => {
return (
<BrowserRouter>
<div>
<Switch>
<Route path="/" exact component={HelloWorld} />
<Route component={NoMatch} />
</Switch>
</div>
</BrowserRouter>
);
};
function NoMatch({ location }) {
return (
<div>
<h3>
No match found
</h3>
</div>
);
}
export default Root;
on '/' this route, it renders HelloWorld component as expected but on other routes for examples abc it displays Cannot GET /abc instead of No match found

This happens because your server is not set up to handle those routes specifically. The server it what determines what exists and what doesn't - react-router is just a tool to exploit it a bit.
Fix 1
You can avoid this issue by importing HashRouter from the react-router-dom package, rather than BrowserRouter.
The result will be routing based on URLs with a #/ prepending the route itself. So your route of / will now actually be #/.
Fix 2
Set up a reverse proxy (nginx) as the intermediary to serve your page from the Node.js server. Use a broad wildcard or get specific if you know you need more specific configurations. It will pass along the request URI/path to node still but not try to use them as separate endpoints each time or read files from the file system.

First you check your react version then after do like this if v5.1 and above
In order to use v6, you'll need to convert all your elements to <Routes>
You want to replace component to element into <Route ...>
example:- App.js
import Home from "./Home";
import About from "./About";
import {BrowserRouter as Router, Route, Routes } from "react-router-dom";
function App() {
return (
<Router>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
export default App;
after that your switch related error maybe gone!
if still not working comment me i will help you

The code works just fine if you used create-react-app for setting up the react project but if you're using webpack configurations for manually setting up the project it requires devServer: {historyApiFallback: true,} for react routes to work.

Have a look into the sandbox it will help you:
https://codesandbox.io/s/py114mqzj0
Please upgrade your dependencies as follows:
"dependencies": {
"react": "16.5.2",
"react-dom": "16.5.2",
"react-router-dom": "4.3.1",
},

This seems to be working fine for me with latest React and react-router-dom.
import { BrowserRouter, Route, Switch } from 'react-router-dom';
const HelloWorld = () => {
return <div>Hello World</div>;
};
const Root = () => {
return (
<BrowserRouter>
<div>
<Switch>
<Route path='/' exact component={HelloWorld} />
<Route component={NoMatch} />
</Switch>
</div>
</BrowserRouter>
);
};
function NoMatch() {
return (
<div>
<h3>No match found</h3>
</div>
);
}
export default Root;
Code sandbox link to try it out

Related

What is the Simple way to use URL params in react-router V6

I am learning to react and the tutorial was way too old so I switched to documentation and articles. But I found it very confusing. (Eg: Switch was removed in V6 But still there in V6 docs).
How to use URL params to render a param to an element? like a heading?
you can use this let {id} = useParams(); for a route with a following path path="/manage-business/:id"
Make sure you install react router dom
npm install react-router-dom
import react router
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
Wrap your components in the Router and Routes imports. This is a must for v6.
<Router>
<Routes>
<Route
exact
path="/any-name"
element={(<componentNameToShow />)}
/>
</Routes>
</Router>
Adding to Michal Šajdík ans
export function Some() {
let { name } = useParams();
return (
<div>
<p>All details about {name}</p>
</div>
)
}
and in your App.js
function App() {
return (
<Router>
<Routes>
<Route path="link/:name" element={<Some/>} />
</Routes>
</Router>
);
}

Content disappears when using the <Route> tag

When ever i add a <Route> tag to the code everything just disappears from the screen!
the code is fine i guess! there is no warnings showing!!
the output before adding the <Route> tags
and when i add a <Route> tag the output is just not there anymore!!!!
the code before adding the tag:
import React from "react";
import "./App.css";
import Header from "./Header";
import { BrowserRouter, Route } from "react-router-dom";
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
</BrowserRouter>
</div>
);
}
export default App;
and the code after is really the same but with adding the tags :
import React from "react";
import "./App.css";
import Header from "./Header";
import { BrowserRouter, Route } from "react-router-dom";
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
<Route></Route>
</BrowserRouter>
</div>
);
}
export default App;
And when i added the tags the content disappeared !
Check you're package.json if the version of react-router-dom is 6 then downgrade it to 5 thats where error comes from.
I know this because now npm install install version 6 as its default version but the tutorials and knowledge about react router on line is still for version 5.
so on your package.json change version from 6 to 5.3.0 and run npm install on your terminal
Since the route tag is empty, you aren't returning anything. You need to pass the path and the component that should be rendered for each path.
In this case considering you have a single path and want to show Hello World!! when the app starts, you should add
<BrowserRouter>
<Route exact path="/" component={App} />
</BrowserRouter>
This will render your App component when you start the app. You can add other components for other routes similarly.
There are few things to do :
a) in react-router-dom v6 Route element should be inside Routes element.
b) instead of component attribute you will have element attribute
c) element attribute should contain jsx like below
<Routes>
<Route exact path="/" element={
<>
<SomeComponent></SomeComponent>
<SomeOtherComponent></SomeOtherComponent>
</>
}>
</Route>
<Route path='/other_address' element={<OtherComponent />}></Route>
</Routes>

react-router-dom Switch needs additional div wrapper

I am using react, react-router-dom and redux to create a simple react application. The package.json file contains:
...
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
...
The project structure is as follow:
- src
- components
- Container
. index.js
+ Customers
+ Greetings
. App.js
- reducers
. customer.js
. index.js
. reducers.js
. Root.js
This is what index.js file looks like:
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import Root from './Root'
import rootReducer from './reducers'
import registerServiceWorker from './registerServiceWorker'
const store = createStore(
rootReducer
)
render(<Root store={ store } />, document.getElementById('root'))
registerServiceWorker()
This is also what is my Root.js file looks like:
import React from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom';
// Import app component
import App from './components/App'
const Root = ({ store }) => (
<Provider store={ store }>
<Router>
<App />
</Router>
</Provider>
)
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
And following represents my App component:
import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom'
// import pages
import Container from './Container';
import Greetings from './Greetings';
import Customers from './Customers';
class App extends Component {
render() {
return (
<Switch>
<div> // <~~ This is where the issue happens
<Route path="/" component={ Container }></Route>
<Route exact path="/" component={ Greetings }></Route>
<Route path="/customers" component={ Customers }></Route>
</div>
</Switch>
);
}
}
export default App;
The problem is when I use <Switch /> as a wrapper of <Route /> tags, it needs an inner <div /> element, otherwise it will not work.
It means that if I remove the <div /> element inside the <Switch /> and wrap the <Route />s directly by <Switch /> the pages will not be rendered, and also no errors or exception throw in the console.
What is the problem? Am I doing wrong somewhere?
Any help is appreciated.
Edit:
Thanks all for useful comments. Here are some important things to consider:
I don't get any error or warning, react script works fine and compiles my code completely.
As you mentioned, only the first <Route> element of a specific path will be rendered, but when I add a <div> element as first level of <Switch> element as a wrapper for all <Route>s it will work fine. But the problem is I don't need the additional <div> element to be rendered. Please consider that I would like the Container component to be rendered in all pages and other components in same path ('/') should be rendered as children of Container.
There are two solutions:
The solution which Raghav mentioned in comments:
It could be done by making the first <Route /> -which I need to be rendered in all pages- as a container component and use it as a wrapper of other <Route /> inside the <Switch > tag. Gist link from #Raghav
<Switch>
<Container>
<Route exact path="/" component={ Greetings }></Route>
<Route path="/customers" component={ Customers }></Route>
</Container>
</Switch>
The second and a newer solution to this problem is using <React.Fragment> tag which is available in newer versions of React. <React.Fragment> will help you to wrap multiple elements in your component instead of using real html tags.
It will not render any extra elements into page
~~> Link to React documentation for Fragment

React Router - route using MemoryRouter and Router together (Some links use memory while others change the url)

I'm using react-router-dom and found the <MemoryRouter> very useful. However, I still want many of the routes to read/write to/from the URL in the browser. How should I go about implementing this?
Thanks
Better to wrap your app in BrowserRouter and inside use MemoryRouter only where you need it
For React Router v5, I think you can nest MemoryRouter within BrowserRouter where needed and detect route changes with useLocation.
This example code worked for me.
import React from 'react';
import {
BrowserRouter, MemoryRouter, Switch, Route,
useLocation
} from 'react-router-dom';
const NestedComponent = () => {
return (
<Switch>
<Route exact path="/">
<p>test</p>
</Route>
</Switch>
);
};
const Component = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/browser-router">
<MemoryRouter>
<NestedComponent/>
</MemoryRouter>
</Route>
<Switch>
</BrowserRouter>
);
};
export default Component;

How to use React Router with Electron?

Using this boilerplate as reference I created an Electron app. It uses webpack to bundle the scripts and express server to host it.
Webpack config is practically same as this and server this.
Electron's script loads:
mainWindow.loadURL('file://' + __dirname + '/app/index.html');
And index.html loads the script hosted by the server:
<script src="http://localhost:3000/dist/bundle.js"></script>
I run electron index.js to build the app and node server to start server which using webpack bundles the scripts.
It works fine, my React component App is mounted. But how I integrate react-router into this?
I implemented it the same way I would in a browser app. I get this error:
[react-router] Location "/Users/arjun/Documents/Github/electron-app/app/index.html" did not match any routes
It is taking file path as the route. Going through the boiler plate code did not help. What am I missing?
Had to Replace BrowserRouter with HashRouter.
import {
HashRouter,
Route
} from "react-router-dom";
And then in my index.js or the entry file of the Electron app I had something like this:
<HashRouter>
<div>
<Route path="/" exact component={ Home } />
<Route path="/firstPage" component={ FirstPage } />
<Route path="/secondPage" component={ SecondPage } />
</div>
</HashRouter>
And then everything just worked.
The reasoning: BrowserRouter is meant for request-based environments whereas HashRouter is meant for file-based environments.
Read more here:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
Another option would be to use hashHistory instead. Actually, in your referenced repo you can see that they're using hashHistory, how about trying that and posting back?
I'm using React Router v4 and didn't want to fallback to the HashRouter, so I solved it with something amongst the likes of:
import { Redirect, BrowserRouter } from 'react-router-dom';
const App = () => (
<BrowserRouter>
<div>
{window.location.pathname.includes('index.html') && <Redirect to="/" />}
</div>
</BrowserRouter>
);
Best option at the time of this answer is to use the MemoryRouter, worked for me :)
What about simply using Switch to default to "/" as follows:
<Switch>
<Route path="/" exact component={Home}/>
<Route path="/foo" component={Foo}/>
<Route path="/bar" component={Bar}/>
<Route render={() => <Redirect to="/"/>}/>
</Switch>
This way, "/index.html" will redirect to "/"
The (current) react-router docs say:
Generally speaking, you should use a <BrowserRouter> if you have a server that responds to requests and a <HashRouter> if you are using a static file server.
An Electron app is basically a static file server.
MemoryRouter can also work, so long as all routing originates from within the React part of the app. It only falls down when you want to navigate to a specific page from the Browser process, e.g. you want to pop up a new window and navigate directly to a "General Preferences" page. In that case, you can do this with HashRouter:
prefsWindow.loadURL(`file://${__dirname}/app/index.html#/general-prefs`);
I don't think there is a way to do that with MemoryRouter (from the Browser process).
Agree with Niekert.
But I believe it is better to handle like this before any route management.
if ( window.location.pathname.includes('index.html') ) {
location.pathname = ROUTES.ROOT;
}
It all depends on what kind of URL you pass to mainWindow.loadURL.
file://...
If you load index.html directly through the file:// protocol, such as
mainWindow.loadURL('file://' + path.join(__dirname, '../index.html#/home'))
then you need to use HashRouter:
<HashRouter>
<Routes>
<Route path='/home' element={<MyHomeScreen/>}>
</Routes>
</HashRouter>
Note that the # in index.html#/home is very important!
Why?
Think about what would happen if you wrote index.html/home. Your computer would try to retrieve a home file inside an index.html directory. The # prevents this, and thus we need to use HashRouter.
http://localhost:3000
If you load index.html from a server such as localhost:3000, then you have two options:
include the #, as in
mainWindow.loadURL('http://localhost:3000#/home')
and use HashRouter exactly as above,
OR
don't include the #, as in
mainWindow.loadURL('http://localhost:3000/home')
and use BrowserRouter like this:
<BrowserRouter>
<Routes>
<Route path='/home' element={<MyHomeScreen/>}>
</Routes>
</BrowserRouter>
Many people prefer to use BrowserRouter in this case because it avoids complicating the URL with a #.
In your main process:
mainWindow.loadURL(resolveHtmlPath('index.html'));
In your renderer process:
import { HashRouter as Router, Routes, Route } from 'react-router-dom';
//...
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/search" element={<Search />} />
<Route
path="/library"
element={<Library />}
/>
</Routes>
</Router>
The call to resolveHtmlPath removed the need for using hashtags for me. Using a BrowserRouter out of the box gave me the warning message in dev tools.
This function is in the Electron React Boilerplate project that you referenced:
import { URL } from 'url';
import path from 'path';
export function resolveHtmlPath(htmlFileName: string) {
if (process.env.NODE_ENV === 'development') {
const port = process.env.PORT || 1212;
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
}
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
}

Categories

Resources