I'm creating a website, and I want the users to be directed to a specific page when they open the site. The page they are going to be directed to depends on if they already logged in. My problem is: the router doesn't work (user is not redirected to any page) and all that appears is a blank page. I've tried to get rid of the routes, but even though, I couldn't display anything on the index page. Maybe the problem is not even the router, but something else.
I never get any error messages. Here are the parts of the code, where I think the problem may be.
_app.js:
import React from 'react'
import { BrowserRouter as Router, Route } from "react-router-dom"
import Novidades from './lancamento'
import SignUp from './signup'
import Croosa from './croosa'
import { AuthProvider } from '../utils/auth'
import PrivateRoute from '../utils/PrivateRoute'
const App = () => {
return(
<AuthProvider>
<Router>
<div>
<PrivateRoute exact path='/lancamento' component={Lancamento} />
<Route exact path='/croosa' component={Croosa}/>
<Route exact path='/signup' component={SignUp} />
</div>
</Router>
</AuthProvider>
)
}
export default App
index.js:
import React from 'react'
import App from './_app'
export default function Home() {
return(
<App/>
)
}
And the PrivateRoute.js, which decides to which page the user is going to be redirected:
import React, { useContext } from "react";
import { Route, Redirect } from "react-router-dom";
import { AuthContext } from "./auth";
const PrivateRoute = ({ component: RouteComponent, ...rest }) => {
const {currentUser} = useContext(AuthContext);
return (
<Route
{...rest}
render={routeProps =>
!!currentUser ? (
<RouteComponent {...routeProps} />
) : (
<Redirect to={"/signup"} />
)
}
/>
)
}
export default PrivateRoute
I would appreciate it if someone could point out my mistake(s).
Next.js uses a filesystem based routing structure.
You have a misunderstanding of how the _app.js file works. It's the parent component that is responsible for rendering the other components that get exported from other pages.
For example: if my _app.js file looks like this:
export default function App({ Component, pageProps }) {
return (
<div>
<p>This was injected by _app.js</p>
<Component {...pageProps} />
<div>
);
}
and my pages/index.js file looks like this:
export default function Hello(){
return <h1>Hello World!</h1>
}
With that setup if I visit the localhost:3000/ then the following will get rendered
<div>
<p>This was injected by _app.js</p>
<h1>Hello World!</h1>
</div>
What you did instead is in your _app.js you ignored the Component property that was passed and so on every page you visit the same content will be rendered. That is:
<AuthProvider>
<Router>
<div>
<PrivateRoute exact path="/lancamento" component={Lancamento} />
<Route exact path="/croosa" component={Croosa} />
<Route exact path="/signup" component={SignUp} />
</div>
</Router>
</AuthProvider>
The reason your index page is blank is because you didn't set up a route for / and so no component will be rendered by react router on that page. Regardless I suggest you stop using react router and start using the built in routing system with next.js
Related
I am trying to make a multi page app with react routing.
I am have some questions as to how I should structure the routing in the react project.
I want to load my component in my app.js file. In my Home component I would like to have the ability to press a button which will take me to the Poems component, I want the code to be clean and structured into components, therefore I dont want to do all this in the app.js file.
If someone would explain to me how to best do this I can from there be able to route around to multiple pages afterwards depending on the page you are on. I dont want to have a global menu currently (I will want that in the Poems component later though).
Here is my current App.js file & Home.jsx component code just for a more easily adjustable experience for you guys!
Currently it is not optimized to work so if anyone knows a good routing solution for my issue, please give me an example of the routing fix.
Thanks alot
/Jacob
import React from 'react'
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'
import './App.scss'
import { Home, Poems, Favourites } from './Pages'
const App = () => {
return (
<Router>
<div className="app">
<Home />
<Routes> {/* I read that the Switch was replaces with Routes */}
<Route path="/" exact component={ Home } />
<Route path="/Poems" component={ Poems } />
<Route path="/Favourites" component={ Favourites } />
</Routes>
</div>
</Router>
)
}
export default App
import React from 'react'
import { Route, BrowserRouter as Router, Routes, Link } from 'react-router-dom'
import { Poems } from './Pages'
import './Home.scss'
const Home = () => {
return (
<Router>
<div>
<h1>Petry For All</h1>
<Routes>
<Route path="/Poems" component={ Poems } />
<Link to="/Poems">Poems</Link>
</Routes>
</div>
</Router>
)
}
export default Home
You don't need to (and actually shouldn't) duplicate the <Router> component in all of the route pages. It is only the root component that is acting as a router. So you can keep the App component the same, and then replace the Home component with the following:
import React from 'react'
import { Poems } from './Pages'
import './Home.scss'
const Home = () => {
return (
<div>
<h1>Petry For All</h1>
<Link to="/Poems">Poems</Link>
</div>
)
}
export default Home
The <Link> component resolves into an anchor element which, when clicked, navigates the user to the route passed into the to property.
I am trying to redirect from my context following a failed update of the state from the a cookie.
import React, { createContext, Component } from 'react';
import { withRouter } from "react-router-dom";
import Cookies from 'universal-cookie';
export const MyContext = createContext();
const cookies = new Cookies();
class MyProvider extends Component {
componentDidMount() {
this.setStateFromCookie();
}
setStateFromCookie = () => {
try {
this.setState({ data: cookies.get('my-cookie')['data'] });
} catch(error) {
console.log(error);
this.props.history.push('/');
}
return
};
render() {
return (
<MyContext.Provider value={{...this.state}}>
{this.props.children}
</MyContext.Provider>
);
}
}
export default withRouter(MyProvider);
I am using a withRouter hook to this.props.history.push('/'), becuase the context is wrapping the router
class MyApp extends Component {
render() {
return (
<BrowserRouter>
<MyProvider>
<div className="MyApp">
<Router>
<Route exact path='/' component={Index} />
<Route exact path='/dashboard' component={Dashboard} />
</Router>
</div>
</MyProvider>
</BrowserRouter>
);
}
}
export default MyApp;
The problem is that the redirect to the home page following the error, but the home page isn't rendering.. I still see the dashboard page.
Any idea what is going on and how to fix this
The issue is that you have a nested Router wrapping your Routes. You need to remove that and then everything will work fine
<BrowserRouter>
<MyProvider>
<div className="MyApp">
<Route exact path='/' component={Index} />
<Route exact path='/dashboard' component={Dashboard} />
</div>
</MyProvider>
</BrowserRouter>
When you use a nested Router, and try to navigate from Provider, the history used by Provider is being provided by BrowserRouter and hence it isn't able to communicate to the Routes whcih are dependent on the inner <Router> component for history.
Using a single router wrapping your components solves this issue
I am a bit lost with this issue for a whole day.
On button click the url changes but does not render the new page and I don't understand why.
I am using react-dom-router 5.2.0
INDEX JS
import {Router} from 'react-router-dom';
import history from './history';
ReactDOM.render(
<React.StrictMode>
<Router history={history}>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
APP JS
import Server from './Server';
import React from 'react';
function App() {
return (
<Server />
);
}
export default App;
SERVER JS
export default class Server extends Component
{
render()
{
return(
<div className="Homepage" >
<h1 className="header">Server</h1>
<button className="button"
onClick={() => history.push('/control')}>
Lets go
</button>
}
</div>
);
}
}
Please Note : I added <Control/> directly in the render method above and it renders the component all well .
CONTROL JS
import React, {Component} from 'react';
import Page2_View from './Page2_View';
export default class Control extends Component
{
constructor(props)
{
super(props);
}
render()
{
return(
<Page2_View/>
);
}
}
Page2_View
import React, {Component} from 'react';
const Page2_View = (props) =>
{
return(
<h1> PAGE 2 VIEW </h1>
);
}
export default Page2_View;
ROUTES JS
import {BrowserRouter as Router, Route, Redirect, Switch} from 'react-router-dom';
const Routes = () =>
{
return(
<Router>
<div>
<Switch>
<Route exact path="/test" component={Server}/>
<Redirect from = '/test' to = '/control'/>
<Route exact path="/control" component={Control}/>
</Switch>
</div>
</Router>
);
}
export default Routes;
HISTORY JS
import {createBrowserHistory as history} from 'history';
export default history();
I appreciate all the help. Thank you
I think the problem is that react-router-dom is not aware of this history.push('/control') you're doing; i.e. if you want to redirect to another route, it should be through react-router, not outside of it.
You have a few options:
Use the useHistory hook: https://reacttraining.com/react-router/web/api/Hooks/usehistory
Your button could be wrapped in a Link component: https://reacttraining.com/react-router/web/api/Link
Get the router through props with the withRouter component, as explained in: Programmatically navigate using react router V4.
I have realized what I was doing wrong and was able to solve my issue.
The key was to understand that the Router module from react-router-dom
comes with three props : path , history, and component.
So in order to redirect a page on button click all I had to do embed all my Routes between tag in the App.js
APP JS
function App() {
return (
<Router>
<Switch>
<Route exact path="/test" component={componentA}/>
<Route exact path="/test2" component={componentB}/>
</Switch>
</Router>
);
}
export default App;
And then you can use button onClick to redirect
COMPONENTA JS
<button variant="secondary"
className="button" size="lg"
onClick={() => this.props.history.push('/test2')}>
RedirectTo
</button>
Hope this will be helpful to others who come across this!
In server.js file instead of button use navlink or link from reactrouter below is a saple code
<NavLink to="/control">control</NavLink>
Import every component to routing component then use router switch and redirect statements like below
import Main from './component/Main'
import Welcome from "./component/welcome"
import { Route, BrowserRouter as Router, Switch,Redirect } from 'react-router-dom'
function App() {
return (
<Router>
<Switch>
<Redirect from="/" to="/home" exact />
<Route exact path="/home" component={Main} />
<Route path="/welcome" component={Welcome} />
</Switch>
</Router>
);}
export default App;
and in your component where you re clicking import link or nav link i prefer using navlink
and use it to redirect to page on click
import { NavLink } from 'react-router-dom'
<NavLink to="/home">home</NavLink>
I am having a problem, I am using react router and I will explain the situation.
I have a form to log in, with /authenticate in the url, if authentication is successed then I go to "/" ( home page ) which is doing good now, and I have two navigation bars, one on the left, the other on the top, now when I click on the links, the url changes but the components are not rendered on clicking them, but if I tap the url on the browser and click enter ( page refreshed ) the component is rendered correctly.
Here is my code :
This is the component rendered after the succesful log in, it is my main application, so The MenuGauche and MenuTop are always rendered :
import React, { Fragment } from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import MenuGauche from "./MenuGauche";
import MenuTop from "./MenuTop";
import Acceuil from "./Acceuil";
import Roles from "./Roles";
const Application = () => {
return (
<Fragment>
<MenuGauche></MenuGauche>
<MenuTop></MenuTop>
<BrowserRouter>
<Switch>
<Route path="/" component={Acceuil}></Route>
<Route path="/roles" component={Roles}></Route>
</Switch>
</BrowserRouter>
</Fragment>
);
};
export default Application;
And here is my top route component ( the default component suggested by react ) :
function App(props) {
return (
<ThemeProvider theme={theme}>
<I18nProvider locale={props.language.language}>
<BrowserRouter>
<Switch>
<Route exact path="/" component={Application}></Route>
<Route path="/authenticate" component={Authentification}></Route>
</Switch>
</BrowserRouter>
</I18nProvider>
</ThemeProvider>
);
}
Why is that not working ? I would like to get any help to solve that, a solution or a proposition!
One solution is provided, it is that have to keep the BrowserRouter only in the top root component, but still nothing!
here is the modification :
import React, { Fragment } from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import MenuGauche from "./MenuGauche";
import MenuTop from "./MenuTop";
import Acceuil from "./Acceuil";
import Roles from "./Roles";
const Application = () => {
return (
<Fragment>
<MenuGauche></MenuGauche>
<MenuTop></MenuTop>
<Route path="/" component={Acceuil}></Route>
<Route path="/roles" component={Roles}></Route>
</Fragment>
);
};
export default Application;
If you feel like I need to provide more code just ask for it.
the problem is that you are using two BrowserRouter component, make sure that is is used once and it is in most top level component in whole application
Problem: When I use history.push(), I can see that browser changes url, but it does not render my component listening on the path. It only renders if I refresh a page.
App.js file:
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import { Provider } from "react-redux";
import PropTypes from "prop-types";
//Components
import LoginForm from "../LoginForm/LoginForm";
import PrivateRoute from "../PrivateRoute/PrivateRoute";
import ServerList from "../ServerList/ServerList";
const App = ({ store }) => {
const isLoggedIn = localStorage.getItem("userToken");
return (
<Router>
<Provider store={store}>
<div className="App">
{isLoggedIn !== true && (
<Route exact path="/login" component={LoginForm} />
)}
<PrivateRoute
isLoggedIn={!!isLoggedIn}
path="/"
component={ServerList}
/>
</div>
</Provider>
</Router>
);
};
App.propTypes = {
store: PropTypes.object.isRequired
};
export default App;
Inside my LoginForm, I am making a request to an API, and after doing my procedures, I use .then() to redirect my user:
.then(() => {
props.history.push("/");
})
What happens: Browser changes url from /login to /, but component listening on / route is not rendered, unless I reload page.
Inside my / component, I use useEffect() hook to make another request to API, which fetches data and prints it inside return(). If I console.log inside useEffect() it happens twice, I assume initial one, and when I store data from an API inside component's state using useState() hook.
EDIT: adding PrivateRoute component as requested:
import React from "react";
import { Route, Redirect } from "react-router-dom";
const PrivateRoute = ({ component: Component, isLoggedIn, ...rest }) => {
return (
<Route
{...rest}
render={props =>
isLoggedIn === true ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: "/login" }} />
)
}
/>
);
};
export default PrivateRoute;
What I tried already:
1) Wrapping my default export with withRouter():
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(LoginForm));
2) Creating custom history and passing it as prop to Router.
react-router-dom version is ^5.0.1. react-router is the same, 5.0.1
You have at two mistakes in your code.
You are not using <switch> component to wrap routes. So all routes are processed at every render and all components from each <route> are rendered.
You are using local store to exchange information between components. But change in local store is invisible to react, so it does not fire component re-rendering. To correct this you should use local state in App component (by converting it to class or using hooks).
So corrected code will look like
const App = ({ store }) => {
const [userToken, setUserToken] = useState(localStorage.getItem("userToken")); // You can read user token from local store. So on after token is received, user is not asked for login
return (
<Router>
<Provider store={store}>
<div className="App">
<Switch>
{!!userToken !== true && (
<Route exact path="/login"
render={props => <LoginForm {...props} setUserToken={setUserToken} />}
/>
)}
<PrivateRoute
isLoggedIn={!!userToken}
path="/"
component={ServerList}
/>
</Switch>
</div>
</Provider>
</Router>
);
};
And LoginForm should use setUserToken to change user token in App component. It also may store user token in local store so on page refresh user is not asked for login, but stored token is used.
Also be sure not to put anything between <Switch> and </Switch> except <Route>. Otherwise routing will not work.
Here is working sample