I am trying to develop react application. The problem is when I use the Route and Switch, it is not working. Actually, nothing is happening. Could anyone please give me a clue about the possible problem here?
Here is my code:
import React, { Component } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import { DISHES } from '../shared/dishes';
import DishDetailComponent from './DishdetailComponent';
import Header from './HeaderComponent';
import Footer from './FooterComponent';
import { Switch, Route, Redirect, BrowserRouter as Router } from 'react-router-dom';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
dishes: DISHES
};
}
render() {
const HomePage = () => {
return(
<Home />
);
}
return (
<div>
<Header />
<Router>
<Switch>
<Route path="/home" Component={HomePage} />
<Route path="/menu" Component={() => <Menu dishes={this.state.dishes} />} />
<Redirect to="home" />
</ Switch>
</Router>
<Footer />
</div>
);
}
}
The obvious mistake is that you have capitalized the "C" in the component prop, so you should call it like component={HomePage}
Here are some other things you could improve upon though:
If you are gonna use an inline function, it is preferable to use the render prop, and if you are gonna use a component directly, preferable to just use Component prop. Moreover:
const HomePage = () => {
return(
<Home />
);
}
is unnecessary as you can just use the Home component directly.
Try this for your render() function:
render() {
return (
<div>
<Header />
<Router>
<Switch>
<Route path="/home" component={Home} />
<Route path="/menu" render={() => <Menu dishes={this.state.dishes} />} />
<Redirect to="home" />
</ Switch>
</Router>
<Footer />
</div>
);
}
Related
The logic seems simple, though I've tried a half-dozen permutations to see if anything changes. I have no idea why react-router is behaving this way:
import React from 'react'
import { View, Text } from 'react-native'
import { observer, inject } from 'mobx-react'
import { NativeRouter, Link, Route, Redirect, Switch } from 'react-router-native'
import Welcome from './welcome'
import Tutorial from './tutorial'
import Plants from './plants'
#inject('store')
#observer
class Main extends React.Component {
render() {
const newUser = true //this.props.store.plants.length === 0
const home = newUser ? '/welcome' : '/plants'
return (
<Switch>
<Route path='/plants' component={Plants} />
<Route path='/tutorial' component={Tutorial} />
<Route path='/welcome' component={Welcome} />
<Redirect to={home} />
<Route path='/' component={Welcome} />
</Switch>
)
}
}
export default Main
The final 'welcome' should be unnecessary, but I've put it there to test: if I remove the then welcome does appear, so it's clearly the that's causing a blank page to render.
This is the render() method of the top-level component:
return (
<Provider store={store}>
<NativeRouter>
<Main />
</NativeRouter>
</Provider>
)
This is based on the example at https://reacttraining.com/react-router/native/guides/philosophy which shows a Switch, Route, and Redirect all being used without an enclosing Router:
const App = () => (
<AppLayout>
<Route path="/invoices" component={Invoices}/>
</AppLayout>
)
const Invoices = () => (
<Layout>
{/* always show the nav */}
<InvoicesNav/>
<Media query={PRETTY_SMALL}>
{screenIsSmall => screenIsSmall
// small screen has no redirect
? <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
</Switch>
// large screen does!
: <Switch>
<Route exact path="/invoices/dashboard" component={Dashboard}/>
<Route path="/invoices/:id" component={Invoice}/>
<Redirect from="/invoices" to="/invoices/dashboard"/>
</Switch>
}
</Media>
</Layout>
)
Use the NativeRouter as the topmost component in your Main component and it will work as expected.
#inject('store')
#observer
class Main extends React.Component {
render() {
const newUser = true //this.props.store.plants.length === 0
const home = newUser ? '/welcome' : '/plants'
return (
<NativeRouter>
<Switch>
<Route path='/plants' component={Plants} />
<Route path='/tutorial' component={Tutorial} />
<Route path='/welcome' component={Welcome} />
<Redirect to={home} />
</Switch>
</NativeRouter>
)
}
}
How to implement conditional routing i.e. if and only if some conditions satisfies, then routing should occur.
For example, if and only if the user enters the correct credentials, login should be successful and the user should be able to see the welcome page.
If we directly hit some URL like localhost:8080/welcome, that should not be navigated to welcome page. The welcome page should only be displayed after login.
How to achieve this, can anyone help me please?
App.js
import React, { Component } from 'react';
import Header from './Header';
class App extends Component{
render(){
return(
<div>
<Header />
</div>
);
}
}
export default App;
Header.js
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import Login from './Login';
import SignUp from './SignUp';
class Header extends Component{
render(){
return(
<div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li><Link to={Login}>Login</Link></li>
<li><Link to={Login}>SignUp</Link></li>
</ul>
</div>
</nav>
</div>
);
}
}
export default Header;
AllRoutes.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Login from './Login';
import SignUp from './SignUp';
import Welcome from './Welcome';
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
<Route exact path="/Welcome" component={Welcome} />
</Switch>
);
}
}
export default AllRoutes;
Welcome.js
import React, { Component } from 'react';
class Welcome extends Component{
render(){
return(
<div>
<h2>Welcome to MainPage..</h2>
</div>
);
}
}
export default Welcome;
To help answer your question, I think you may need to also ask how that route should get blocked. Looking through the example above, you don't yet have a mechanism that helps answer the question of "should I be able to visit this page". That might come from state, redux, or some other means of determining if the user is logged in.
Since react-router is just plain React (one of my favorite parts!!) you have all the tools available to you that you would to conditionally show any part of your React app.
Here are a couple examples of how you might achieve this (by no means is this exhaustive. Be creative! It all depends on your requirements and the tools you are using)
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
{ this.state.authenticated &&
<Route exact path="/Welcome" component={Welcome} />
}
</Switch>
);
}
}
One of my favorite ways to accomplish this is creating a ProtectedRoute component
class ProtectedRoute extends Component {
render() {
const { component: Component, ...props } = this.props
return (
<Route
{...props}
render={props => (
this.state.authenticated ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
class AllRoutes extends Component {
render() {
return (
<Switch>
<Route path='/login' component={Login} />
<ProtectedRoute path='/welcome' component={Welcome} />
</Switch>
)
}
}
While I didn't include any specific logic to how state.authenticated was set, this may come from anywhere (by no means does it needs to come from state). Do your best to answer the question of "how do I determine whether a user is authenticated" and use that mechanism as the means to handle route authentication.
For that you need to break the entire app into two parts, normally accessible and protected part. Protected part will be accessible only after successful login.
To achieve that functionality, create a wrapper of protected part, and define its routes with path='/', and put the condition inside that. All the protected routes should be defined inside that wrapper component. If anyone try to access those routes without login, wrapper will redirect them to login page.
Like this:
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
<Route path="/" component={AppWrapper} />
</Switch>
);
}
}
AppWrapper Component (assuming you are using some way to maintain whether user is logged-in or not, so put the proper check in if condition):
import { Redirect } from 'react-router-dom'
class AppWrapper extends Component{
render(){
if(/*not login*/)
return <Redirect to="/login" />
return(
<div>
App wrapper
<Route path='/Welcome' component={Welcome} />
</div>
);
}
}
I would like to join the party with simple solution.
Just conditional render in the component prop in as follows:
<Router>
<Navigation />
<Switch>
<Route
exact
path="/"
component={
loading
? () => <div>Loading posts...</div>
: () => <Home posts={posts} />
}
/>
<Route path="/login" component={Login} />
</Switch>
</Router>
Here i am trying to fetch some data from an api when it fetched (loading) should be false and renders Home component.
You can do something like:
let redirectToUrl;
if ( not logged in ) //check condition
{
redirectToUrl = <Redirect to={loginPage}/>;
}
and use the same:
<Router>
<div>
{redirectToUrl}
<Switch>
<Route />
</switch>
</div>
</Router>
For the same you need to import from react-router-dom:
import {
BrowserRouter as Router,
Route,
browserHistory,
Redirect,
Link,
Switch
} from "react-router-dom";
Best way is to create a HOC.
Considering you are maintaining auth state in your redux store. Or else you can check with your own variable.
Create requireAuth.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
export default function(ComposedComponent) {
class Authentication extends Component {
static contextTypes = {
router: React.PropTypes.object
}
componentWillMount() {
if (!this.props.authenticated) {
this.context.router.push('/');
}
}
componentWillUpdate(nextProps) {
if (!nextProps.authenticated) {
this.context.router.push('/');
}
}
render() {
return <ComposedComponent {...this.props} />
}
}
function mapStateToProps(state) {
return { authenticated: state.auth.authenticated };
}
return connect(mapStateToProps)(Authentication);
}
Now in the routes you can use this hoc and pass the component.
import RequireAuth from './requireAuth';
...
<Route exact path="/Welcome" component={RequireAuth(Welcome)} />
The best and simple thing you can do is to create a state variable login and route based on the boolean values. the logic to set is up to you. i can show an example of simple routing based on condition. I store my pages in a array and use the map function to switch to different routes. For an example I have inserted my DesignerHome.js for your reference
This is my App.js
import React,{Component} from 'react';
import{BrowserRouter as Router,Switch,Route,Redirect,} from 'react-router-dom'
import MainHome from './MainHome'
import DesignerHome from './designer/DesignerHome'
export default class App extends Component{
constructor(){
super()
this.state={
login : true,
r_page :[
{
path :'/designerhome',
component : DesignerHome,
},]
}
}
render(){
return(
<Router>
<Switch >
<Route path='/' exact component={MainHome}/>
{this.state.r_page.map((item , i)=>(this.state.login?
<Route exact {...item}/> : <Redirect to="/" /> ))}
</Switch>
</Router>
)
}
}
This is my DesignerHome.js
import React,{Component} from 'react';
export default class DesignerHome extends Component{
render(){
return(
<div>
designer home
</div>
)
}
}
create a state for authentication. based on that navigate to the page.
Also I used render instead of component in Route.
import React, { Fragment, useState, useEffect } from "react";
import Dashboard from "./components/Dashboard";
import Login from "./components/Login";
import Register from "./components/Register";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
useEffect(() => {
isAuth(); // to be implemented
}, []);
return (
<Fragment>
<Router>
<div className="container">
<NavigationCard />
<Switch>
<Route
exact
path="/login"
render={(props) =>
!isAuthenticated ? (
<Login {...props} setAuth={setAuth} />
) : (
<Redirect to="/dashboard" />
)
}
/>
<Route
exact
path="/register"
render={(props) =>
!isAuthenticated ? (
<Register {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
)
}
/>
<Route
exact
path="/dashboard"
render={(props) =>
isAuthenticated ? (
<Dashboard {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
)
}
/>
</Switch>
</div>
</Router>
</Fragment>
);
}
export default App;
I am using react router v4 and I'm trying to wrap my head around a react-router / redux / HOC related issue. I have a higher order component working. The HOC itself is connect()-ed to redux store. This approach works perfectly if I wire it up in a <Route /> via a component prop: <Route path="/profile" component={ withAuth(Profile) } /> does work.
However, when I try to do the same with a <Route /> and a render prop it does not work: <Route path="/profile" render={ () => withAuth(Profile) } /> The console throws "Route.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object." It does work when I omit the HOC: <Route path="/profile" render={ () => <Profile /> } /> so I suspect a problem with the HOC but I can't find it.
The reason I'm trying to use render is I'd like to pass additional props to the HOC. Besides it bugs me that I can't find the bug.
Can anybody with a fresh eye have a look and put me on the right path? Thanks!
/* === app.js === */
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import Header from './header';
import Home from './home';
import Content from './about';
import Profile from './profile';
import withAuth from './withAuth';
import store from '../reducers/store';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div className="mdl-grid">
<Header />
<main className="mdl-layout__content">
<div className="page-content">
<Route path="/" exact component={Home} />
<Route path="/about" component={Content} />
<Route path="/profile" render={ () => withAuth(Profile) } />
</div>
</main>
</div>
</Provider>
)
}
}
/* === withAuth.js (Higher order component) === */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
const HOC = WrappedComponent => {
return class extends Component {
render() {
if (this.props.auth) {
return <WrappedComponent authenticated={this.props.auth} {...this.props} />
} else {
return <Redirect to="/" />
}
}
}
}
function mapStateToProps({ auth }) {
return { auth };
}
export default WrappedComponent => connect(mapStateToProps)( HOC(WrappedComponent) );
The reason it doesn't work is because, here
<Route path="/profile" render={ () => withAuth(Profile) } />
render is actually assigned a function withAuth and not the returned value. What you need to do is
const AuthProfile = withAuth(Profile);
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div className="mdl-grid">
<Header />
<main className="mdl-layout__content">
<div className="page-content">
<Route path="/" exact component={Home} />
<Route path="/about" component={Content} />
<Route path="/profile" render={ (props) => <AuthProfile {...props}/> } />
</div>
</main>
</div>
</Provider>
)
}
}
The difference between
render={ () => withAuth(Profile) }
and
render={ (props) => <AuthProfile {...props}/> }
is that in the first case its an arrow function that is bound to the context. Whereas in the second case its a function returning a component
I think your problem is with the way you use <Redirect />, you have to put it in <Route />. Look at this example:
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
I'm pulling my hair out trying to render multiple layouts with React Router v4.
For instance, I'd like pages with the following paths to have layout 1:
exact path="/"
path="/blog"
path="/about"
path="/projects"
and the following paths to have layout 2:
path="/blog/:id
path="/project/:id
Effectively what's being answered here but for v4: Using multiple layouts for react-router components
None of the other answers worked so I came up with the following solution. I used the render prop instead of the traditional component prop at the highest level.
It uses the layoutPicker function to determine the layout based on the path. If that path isn't assigned to a layout then it returns a "bad route" message.
import SimpleLayout from './layouts/simple-layout';
import FullLayout from './layouts/full-layout';
var layoutAssignments = {
'/': FullLayout,
'/pricing': FullLayout,
'/signup': SimpleLayout,
'/login': SimpleLayout
}
var layoutPicker = function(props){
var Layout = layoutAssignments[props.location.pathname];
return Layout ? <Layout/> : <pre>bad route</pre>;
};
class Main extends React.Component {
render(){
return (
<Router>
<Route path="*" render={layoutPicker}/>
</Router>
);
}
}
simple-layout.js and full-layout.js follow this format:
class SimpleLayout extends React.Component {
render(){
return (
<div>
<Route path="/signup" component={SignupPage}/>
<Route path="/login" component={LoginPage}/>
</div>
);
}
}
So, for this you should use render function (https://reacttraining.com/react-router/core/api/Route/render-func)
A really good article that helped me: https://simonsmith.io/reusing-layouts-in-react-router-4/
In the end you will be use something like this:
<Router>
<div>
<DefaultLayout path="/" component={SomeComponent} />
<PostLayout path="/posts/:post" component={PostComponent} />
</div>
</Router>
I solved this problem utilizing a bit of both of your solutions:
My Routes.js file
import BaseWithNav from './layouts/base_with_nav';
import BaseNoNav from './layouts/base_no_nav';
function renderWithLayout(Component, Layout) {
return <Layout><Component /></Layout>
}
export default () => (
<Switch>
{/* Routes with Sidebar Navigation */}
<Route exact path="/" render={() => renderWithLayout(Home, BaseWithNav)} />
{/* Routes without Sidebar Navigation */}
<Route path="/error" render={() => renderWithLayout(AppErrorMsg, BaseNoNav)} />
<Route path="/*" render={() => renderWithLayout(PageNotFound, BaseNoNav)} />
</Switch>
)
Base.js (where routes get imported)
export default class Base extends React.Component {
render() {
return (
<Provider store={store}>
<Router>
<Routes />
</Router>
</Provider>
)
}
}
Layouts
BaseWithNav.js
class BaseWithNav extends Component {
constructor(props) {
super(props);
}
render() {
return <div id="base-no-nav">
<MainNavigation />
<main>
{this.props.children}
</main>
</div>
}
}
export default BaseWithNav;
BaseNoNav.js
class BaseNoNav extends Component {
constructor(props) {
super(props);
}
render() {
let {classes} = this.props;
return <div id="base-no-nav">
<main>
{this.props.children}
</main>
</div>
}
}
export default BaseNoNav;
I hope this helps!
I know i am replying late but it's easy to do that, i hope it will helps to newbie.
i am using React 4
Layout.js
export default props => (
<div>
<NavMenu />
<Container>
{props.children}
</Container>
</div>
);
LoginLayout.js
export default props => (
<div>
<Container>
{props.children}
</Container>
</div>
);
Now finally we have our App
App.js
function renderWithLoginLayout(Component, Layout) {
return <LoginLayout><Component /></LoginLayout>
}
function renderWithLayout(Path, Component, Layout) {
return <Layout><Route path={Path} component={Component} /></Layout>
}
export default () => (
<Switch>
<Route exact path='/' render={() => renderWithLayout(this.path, Home, Layout)} />
<Route path='/counter' render={() => renderWithLayout(this.path, Counter, Layout)} />
<Route path='/fetch-data/:startDateIndex?' render={() => renderWithLayout(this.path, FetchData, Layout)} />
<Route path='/login' render={() => renderWithLoginLayout(Login, LoginLayout)} />
</Switch>
);
If < notFound /> rout get executed (i.e : if not found page get rendered I want to add class to footer component.
Below is my index.jsx render function
render() {
return (
<div>
<Navbar />
<div className="pageData">
{this.props.children}
// <notFound/> component will get rendered here
</div>
<Footer/>
<LoginPopup />
</div>
)
}
Below is my routes.jsx
import React from 'react'
import { Route, IndexRoute } from 'react-router'
import App from 'layout/app'
import Home from 'pages/home'
import MyWagers from 'containers/myWagersContainer'
import Wagers from 'containers/wagersContainer'
import NotFound from 'pages/notFound'
const ROUTES = (
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path="/wagers(/:trackCode)(/:raceNum)" component={Wagers} >
<Route path="*" component={() => (<NotFound status = "404" />)}/>
</Route>
<Route path="/mywagers" component={MyWagers} />
<Route path="*" name = "notFound" component={() => (<NotFound status = "404" />)}/>
</Route> )
export default ROUTES
So can we set something globally or can we get route name so we can add class in footer component as per < notFound / > component get rendered
Use a callback that you pass to this.props.children and then in <NotFound />
componentWillMount() {
this.props.setFooterClass('myclass');
}
componentWillUnmount() {
this.props.setFooterClass('');
}
in index.js:
<NotFound setFooterClass={myClass => this.setState({ footerClass: myClass })} />
and
<Footer className={this.state.footerClass} />
Pass props to children