React Route Redirect - javascript

I read all possible thread about programmatically redirection in React but I couldn't make it working.
My code looks like this (I removed as much I could to make it shorter):
import React from 'react';
import {render} from 'react-dom';
import {BrowserRouter as Router, Route, Switch} from "react-router-dom";
import {Login} from "./login/Login";
class App extends React.Component {
login(loginResponse) {
...
# Here I want to redirect but it does not work
this.props.history.push('/some-path/');
}
render() {
return (
<Router>
<div>
<div id="content">
<Messages />
<Switch>
<Route path="/" exact={true} component={Welcome} />
...
<Route path="/portal/login" render={() => <Login sendMessage={this.sendMessage} />} />
</Switch>
</div>
</div>
</Router>
);
}
}
render(<App/>, document.getElementById("app"));
I'd like to redirect when the login method is called to a different path.
With this.props.history.push('/some-path/'); I get Cannot read property 'push' of undefined.
In most of the places I read about withRouter but I could not make it work.
What am I missing to get the history?
Or what's the easiest to make the redirect work?
Thanks in advance

I think the main problem here is that I was trying to implement the redirect on the top level component.
I ended up moving a bit of login around and do the redirect from the Login component that is rendered thanks to the Route component therefore can access the history.
This is the code I used:
main.js
import React from 'react';
import {render} from 'react-dom';
import {BrowserRouter as Router, Route, Switch} from "react-router-dom";
import {Login} from "./login/Login";
class App extends React.Component {
render() {
return (
<Router>
<div>
<div id="content">
<Messages />
<Switch>
<Route path="/" exact={true} component={Welcome} />
...
<Route path="/portal/login" render={(router) => <Login router={router} sendMessage={this.sendMessage} />} />
</Switch>
</div>
</div>
</Router>
);
}
}
render(<App/>, document.getElementById("app"));
Login.js
import React from 'react';
export class Login extends React.Component {
constructor(props) {
super(props);
this.onLoginResponse = this.onLoginResponse.bind(this);
}
onLoginResponse(loginResponse) {
...
this.props.router.history.push('/portal/');
}
render() {
return (
<div id="login-form">
Login page ...
</div>
);
}
}
The nice thing of this is that now the code for handling a login response is in the Login page.
But as I wanted to store the user who logged in into the main app status I needed to do some extra work.

Related

BrowserRouter react-router-dom not showing components [duplicate]

This question already has answers here:
React App goes blank after importing React-Router-Dom
(3 answers)
Closed 2 months ago.
I'm trying to use BrowserRouter to make a fixed NavBar with page content changing when you go in another page.
This is my App.js:
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import React from 'react';
import Homepage from './components/Homepage/Homepage';
import Dashboard from './components/Dashboard/Dashboard';
import Preferences from './components/Preferences/Preferences';
import { Navbar, NavLink, NavMenu, NavTitle } from './components/Nav/Navbar';
function App() {
return (
<div>
<BrowserRouter>
<Navbar>
<NavMenu>
<NavTitle>Application</NavTitle>
<NavLink to={'/dashboard'}>
Dashboard
</NavLink>
<NavLink to={'/preferences'}>
Preferences
</NavLink>
</NavMenu>
</Navbar>
<div>
<Routes>
<Route path='/' exact element={Homepage} />
<Route path='/dashboard' element={Dashboard} />
<Route path='/preferences' element={Preferences} />
</Routes>
</div>
</BrowserRouter>
</div>
);
}
export default App;
This is Dashboard.js:
import React from 'react';
export default class Dashboard extends React.Component {
constructor() {
super();
console.log("Dashboard")
};
render() {
return(
<h2>Dashboard</h2>
)
};
}
The others page component are the same as this one.
The NavBar is shown, but not the content when I change path.
I tried to search online but seems nobody had my problem, so I'm wrong something.
use Switch instead of routers
<Switch>
<Route path='/' exact element={Homepage} />
<Route path='/dashboard' element={Dashboard} />
<Route path='/preferences' element={Preferences} />
</Switch>

React routing using components (PageRenderer)

I'm trying to do basic routing in React. Usually what I have done, and what I will mention later on, is use element={<some page>}. But currently I want to learn and experiment what other options there are, so I came across components where you insert a function. I have followed a tutorial and I did the exact same, except the tutorial uses an older version of router dom so it doesn't use Routes.
Here is the code:
App.js:
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'
import Register from './pages/register';
import Login from './pages/login';
import PageRender from './PageRender';
function App() {
return (
<Router>
<input type='checkbox' id='theme'/>
<div className="App">
<div className="main">
<Routes>
<Route exact path="/:page" component={PageRender}/>
<Route exact path="/:page/:id" component={PageRender}/>
</Routes>
</div>
</div>
</Router>
);
}
export default App;
PageRender.js:
import React from 'react'
import { useParams } from 'react-router'
import NotFound from './components/NotFound'
const generatePage = (pageName) => {
const component = () => require(`./pages/${pageName}`).default
try {
return React.createElement(component())
} catch (err) {
return <NotFound />
}
}
const PageRender = () => {
const {page, id} = useParams()
let pageName = "";
if(id){
pageName = `${page}/[id]`
}else{
pageName = `${page}`
}
return generatePage(pageName)
}
export default PageRender
The login and register js are just basic arrow functions which display login or register (still didn't come to that part). What I want to do is when I enter the url, let's say for instance: http://localhost:3000/register, it sends me to register page and if I enter a wrong path it will send me to the "NotFound" page. But sadly, it doesn't work. I know I can work around this problem if I simply do this:
<Route exact path="/login" element={<Login/>}/>
This method works, but currently I'm in the process of learning and I'm curious why this method didn't work.
I was able to get your code working in react-router-dom v5, the trick was importing the components once in App so they are built/transpiled. The PageRender component worked as-is.
RRDv5
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import './pages/register';
import "./pages/login";
import PageRender from "./PageRender";
export default function App() {
return (
<Router>
<input type="checkbox" id="theme" />
<div className="App">
<div className="main">
<Switch>
<Route path="/:page/:id" component={PageRender} />
<Route path="/:page" component={PageRender} />
</Switch>
</div>
</div>
</Router>
);
}
RRDv6 - Swap the Switch component to the Routes component, and switch to using the element prop instead of the component prop to render the PageRender component as JSX.
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import './pages/register';
import "./pages/login";
import PageRender from "./PageRender";
export default function App() {
return (
<Router>
<input type="checkbox" id="theme" />
<div className="App">
<div className="main">
<Routes>
<Route path="/:page/:id" element={<PageRender />} />
<Route path="/:page" element={<PageRender />} />
</Routes>
</div>
</div>
</Router>
);
}

React Where to place login page for a one page app with a side menu

I have a react web app with a sidemenu. Whenever a user clicks on the link in the sidemenu, they are routed to a page that is rendered at the right side of the sidemenu. My question is, how do I do login for such a usecase seeing as any page I route to renders to the right of the sidemenu. I want the login page to be full screen without the side menu showing. This is what App.js looks like.
import React, { Component } from "react";
import { HashRouter } from "react-router-dom";
import Navigation from "./pages/General/components/Navigation";
import SideMenu from "./pages/General/components/SideMenu";
import "../src/css/App.css";
class App extends Component {
render() {
return (
<div>
<HashRouter>
<div className="main-wrapper">
<SideMenu />
<Navigation />
</div>
</HashRouter>
</div>
);
}
}
export default App;
Here is Navigation.js
import React from "react";
import { Route } from "react-router-dom";
import CalendarPage from "../../Calendar/CalendarPage";
import DoctorsList from "../../Doctors/DoctorsList";
import PatientsList from "../../Patients/PatientsList";
import AdminUsersList from "../../AdminUsers/AdminUsersList";
import SpecialitiesList from "../../Specialities/SpecialitiesList";
const Navigation = () => {
return (
<div className="mainarea">
<Route exact path="/" component={CalendarPage} />
<Route exact path="/scheduler" component={CalendarPage} />
<Route exact path="/doctors" component={DoctorsList} />
<Route exact path="/patients" component={PatientsList} />
<Route exact path="/admin-users" component={AdminUsersList} />
<Route exact path="/specialities" component={SpecialitiesList} />
</div>
);
};
export default Navigation;
The best solution I can figure out in terms of a clean design, is to implement another router in your App.jsx, because you are implementing the routing inside your component, and you need another one for your login page.
Then, your App.jsx could be like this:
import React, { Component } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import LogIn from "./pages/General/components/Login";
import HomePage from "./pages/General/components/HomePage";
import "../src/css/App.css";
class App extends Component {
render() {
return (
<div>
<Switch>
<Route path={'/login'} component={LogIn} />
<Route path={'/'} component={HomePage} />
<Redirect to="/" />
</Switch>
</div>
);
}
}
export default App;
Then, for your HomePage do the following
import React, { Component } from "react";
import { HashRouter } from "react-router-dom";
import Navigation from "./pages/General/components/Navigation";
import SideMenu from "./pages/General/components/SideMenu";
import "../src/css/App.css";
class HomePage extends Component {
render() {
return (
<div>
<HashRouter>
<div className="main-wrapper">
<SideMenu />
<Navigation />
</div>
</HashRouter>
</div>
);
}
}
export default HomePage;
I hope it helps!
Here is my solution, it not exactly a solution, but it will give you a basic idea on how to implement this.
The idea is to place the Login component in app.js, and conditionally display it if the user is logged in.
You will have to pass a handler function to login component through which you will be able to control app.js state.
When login will be sucessfull, u can show the Navigation and Sidemenu component.
import { Fragment } from "react";
import Login from "path/to/login";
class App extends Component {
state = { isLoggedIn: false };
loginHandler = () => {
this.setState({
isLoggedIn: true
});
};
render() {
return (
<div>
<div className="main-wrapper">
{isLoggedIn ? (
<Fragment>
<SideMenu />
<Navigation />
</Fragment>
) : (
<Login loginHandler={this.loginHandler} />
)}
</div>
</div>
);
}
}
Also you need write a separate router file, which will contain the main app.
This is used to show the app component when navigated to /
import React from 'react';
import { HashRouter, Route } from 'react-router-dom';
import App from './app';
const MainRoute = () => (
<HashRouter>
<Route path="/" component={App} />
</HashRouter>
);
export default MainRoute;

not being able to pass url parameters in react.js

I am working on a React.js web app , for some reasons I am not able to pass url parameters.
An example is being shown below:
Routes:
import React from "react";
import {BrowserRouter, Route, Switch, Redirect} from 'react-router-dom';
import Helloworld from './components/helloworld/helloworld.component';
import SecondView from './components/secondview/secondview.component';
import ThirdView from "./components/thirdview/thirdview.component";
const AppRoutes = () => (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Helloworld}/>
<Route path='/secondview' component={SecondView}/>
<Route path='/thirdview' component={ThirdView}/>
<Route path='/thirdview/:number' component={ThirdView}/>
<Redirect from='*' to='/' />
</Switch>
</BrowserRouter>
);
export default AppRoutes;
Secondview Component
import React from 'react';
import {Link, withRouter} from 'react-router-dom';
class SecondView extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log("I am being called SecondView component...");
}
render() {
return (
<div className={"boxDiv"}>
<p>Second View</p>
<Link to={{pathname: '/thirdview/7'}}> GO to third view with parameter value. </Link>
</div>
);
}
}
export default withRouter(SecondView);
Thirdview comopnent:
import React from 'react';
import { Route, Link,withRouter } from 'react-router-dom';
import FourthView from "../fourthview/fourthview.component";
class ThirdView extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
}
render() {
console.log(this.props.match.params);
return (
<div className={"boxDiv"}>
<p>Third View</p>
{ console.log(this.props)}
<h1>parameter passed: (#{this.props.params.number})</h1>
</div>
);
}
}
export default withRouter(ThirdView);
What I want, is to be able to get /:number value on my thirdview component! Anyone knows how to achieve such thing in React.js? Are there other ways doing this?
What I get is empty object!
I think it should be:
{this.props.match.params.number}
instead of
{this.props.params.number}
and you don't need withRouter on ThirdView and SecondView as are passed to Route already.
EDIT:
Since you want to render the same component for /thirdview and /thirdview/7 use optional param matcher
<Switch>
<Route exact path='/' component={Helloworld}/>
<Route path='/secondview' component={SecondView}/>
<Route path='/thirdview/:number?' component={ThirdView}/>
<Redirect from='*' to='/' />
</Switch>

React Router v4 not rendering components

Having an issue with React Router v4 rendering components. On initial load of the application it will render the correct component corresponding to the URL However, any subsequent Link clicks will not render the desired component.
Libraries
React Router: 4.2.2
React: 15.6.1
React DOM: 15.6.1
-- just to mention libraries in case of impact --
React Redux: 5.0.6
Redux: 3.7.2
Material UI: 0.19.0
Going to omit some imports for sake of brevity
Site Structure
index.jsx
|
App.jsx
|
Auth.jsx
|
Layout.jsx
<Routes />
index.jsx
import React from 'react';
import store from './store.js';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
const history = createBrowserHistory();
import App from './containers/App.jsx';
ReactDOM.render(
<Provider store={store}>
<MuiThemeProvider muiTheme={muiTheme}>
<BrowserRouter>
<App />
</BrowserRouter>
</MuiThemeProvider>
</Provider>,
document.getElementById('root')
);
App.jsx
import React, { Component } from 'react';
import Auth from '../components/Auth.jsx';
class App extends Component {
render() {
return <Auth />;
}
}
Auth.jsx
import React from 'react';
import { Route } from 'react-router-dom';
import Layout from './Layout.jsx';
export default function Auth(props) {
//this Has a render function to render a Loader, Error Page, or the Layout
return <Layout />;
}
Layout.jsx
There's more complexity involved here with rendering out the entire application. I'm going to leave the other layout components commented out and just have some Links and a Switch component to get that working before making items more modular.
import React, { Component } from 'react';
import { Link, Switch, Route } from 'react-router-dom';
import Overview from './views/overview/Overview.jsx';
import Home from './views/home/Home.jsx';
export default class Layout extends Component {
render() {
return (
<div className="layout">
{/* <TopBar /> */}
{/* <AppBar/> */}
{/* <Drawer>
<MainMenu/>
</Drawer> */}
<Link to="/">HOME</Link>
<Link to="/overview">Overview</Link>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/overview" component={Overview} />
</Switch>
{/* <Routes /> */}
{/* <Footer /> */}
</div>
);
}
}
Routes.jsx
This is what I'm intending the routes components to look like.
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './views/home/Home.jsx';
import Overview from './views/overview/Overview.jsx';
import Admin from './views/admin/Admin.jsx';
import NotFound from './NotFound.jsx';
export default function Routes(props) {
return (
<Switch>
<Route path="/" exact component={Home} />
<Route path="/overview" component={Overview} />
<Route path="/admin" component={Admin} />
<Route component={NotFound} />
</Switch>
);
}
Is there something I'm missing to get components to render clicking Link? I'm not getting any console errors or anything telling me there's an issue. So not sure if components are not wrapped correctly or what may be causing the issue.
Looks like what was happening is that with Redux integrations was blocking updates.
Need to:
import {withRouter} from 'react-router-dom';
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App))'
Documentation
You can also use pure: false in connect.
https://github.com/reactjs/react-redux/blob/master/docs/troubleshooting.md#my-views-arent-updating-when-something-changes-outside-of-redux

Categories

Resources