In my application, when I change from one page to another the page is being kept at the same point it was on the previous page. I want to make it go to the top when I swap pages.
The react-router documentation has a solution: https://reactrouter.com/web/guides/scroll-restoration
I implemented it inside a component called ScrollToTop and wrapped my whole application with it, but everything inside it just don't get rendered. I have no idea why.
ScrollToTop:
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
App:
import React from 'react';
import './App.css';
import Layout from './containers/Layout/Layout'
import MainPageConfig from './containers/MainPageConfig/MainPageConfig'
import ScrollToTop from './HOC/ScrollToTop'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
function App() {
return (
<BrowserRouter >
<Layout>
<ScrollToTop>
<Switch>
<Route path="/" exact component={MainPageConfig} />
</Switch>
</ScrollToTop>
</Layout>
</BrowserRouter>
);
}
export default App;
I also tried the suggestions of this post: react-router scroll to top on every transition
In both cases I get the same result.
How could I solve this?
P.S. I also tried to put ScrollToTop outside Layout, but nothing changes.
Can you try the below
<BrowserRouter >
<Layout>
<ScrollToTop />
<Switch>
<Route path="/" exact component={MainPageConfig} />
</Switch>
</Layout>
</BrowserRouter>
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop({children}) {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return <>{children}</>;
}
just pass children down the tree
Related
My page is becoming unresponsive when I try to navigate using useNavigate or a Link in my component. After clicking a button or link, the url will change, and the javascript inside the target component will execute, but the page does not re-render. useNavigate works with other components in my app, so I'm not quite sure what the issue is. Any input would be appreciated!
Dashboard.js:
import { useNavigate, Link } from "react-router-dom";
const Dashboard = () => {
console.log("hello from dashboard");
let navigate = useNavigate();
return (
<div>
<Link className="pl-20" to="/test">
test
</Link>
</div>
);
};
export default Dashboard;
Test.js:
import React from "react";
const Test = () => {
console.log("hello from test page");
return <div>this is the test page</div>;
};
export default Test;
App.js:
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Dashboard from "./pages/Dashboard";
import Test from "./pages/Test";
function App() {
return (
<>
<Nav />
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/test" element={<Test />} />
</Routes>
</>
);
}
gif showing the issue
you gave pl-20 class on Dashboard. but not on Test component.
Please check.
I am running an issue where, regardless of what URL I am putting into my browser, I keep getting routed to my main page. I've posted the code below for you to take a look, but my goal is to have my browser take me to my drivers.jsx component when the URL is localhost:3000/drivers. Currently, when I go to localhost:3000/drivers, it renders my _app.jsx component instead :(. Can someone help me understand why I can never render the Drivers component (in drivers.jsx) when I am at localhost:3000/drivers?
index.jsx:
import { BrowserRouter as Router, Routes, Switch, Route } from 'react-router-dom';
import MyApp from './_app.jsx';
import Drivers from './drivers.jsx'
import React, { Component } from 'react'
import { Link } from "../routes.js"
class Home extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/drivers' element = {<Drivers />}> </Route>
<Route exact path='/' element = {<MyApp />}> </Route>
</Switch>
</Router>
);
}
}
export default Home;
_app.jsx
import React, { Component } from 'react';
import { useLoadScript } from '#react-google-maps/api';
import Map from '../components/map.jsx';
import "../styles/globals.css";
const MyApp = () => {
const libraries = ['places'];
const {isLoaded} = useLoadScript({
googleMapsApiKey: XXXXXXXXXXXXXX,
libraries
});
if (!isLoaded) return <div>Loading...</div>;
return (
<Map />
);
}
export default MyApp;
drivers.jsx
import React, { Component } from 'react';
class Drivers extends Component {
render() {
return (
<div>TEST</div>
);
}
}
export default Drivers;
I've tried putting the routing logic inside _app.jsx instead, but that causes an incredible amount of errors. My thought is index.js should host all the routing logic, but it shouldn't keep rendering MyApp instead of Drivers when the route is "localhost:3000/drivers".
if your react-router-dom version is
6.4.3
then the switch component dosen't work try changing code to this
instead of using Switch. wrap Route inside Routes
<Router>
<Routes>
<Route path='/drivers' element = {<Drivers />}> />
<Route exact path='/' element = {<MyApp />}> />
</Routes>
</Router>
like this
As an exercise, I'm making a react app (still learning React) that implements a login system with firebase. Of course, to implement such a feature, react router is necessary and I have successfully implemented it. However, once the user logs in he should be able to see a sidebar alongside other content that is changed dynamically. I now need to again use react router to change those pages when a user clicks on a specific item in the sidebar without having to render the sidebar with each component. I have read the docs for nesting routers but just cant get it to work. Any help is greatly appreciated.
Here's the code:
App.js:
import "./App.css";
import LoginForm from "./components/LoginForm";
import { AuthProvider } from "./contexts/AuthContext";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Dashboard from "./components/Dashboard";
import PrivateRoute from "./components/PrivateRoute";
function App() {
return (
<div className="App">
<Router>
<AuthProvider>
<Switch>
<PrivateRoute exact path="/" component={Dashboard} />
<Route path="/login" component={LoginForm} />
</Switch>
</AuthProvider>
</Router>
</div>
);
}
export default App;
Dashboard.js:
import React from "react";
import { useAuth } from "../contexts/AuthContext";
import { useHistory } from "react-router";
import Sidebar from "./Sidebar/Sidebar";
import { useRouteMatch } from "react-router";
const Dashboard = () => {
const { currentUser, logout } = useAuth();
const history = useHistory();
let { path, url } = useRouteMatch();
const handleLogout = async () => {
try {
await logout();
history.push("/login");
} catch (error) {
console.log(error);
}
};
if (!currentUser) return null;
return (
<div>
<Sidebar logout={handleLogout} />
</div>
);
};
export default Dashboard;
PS. I'm quite new to react and any tip/critique is welcome
You can always conditionally render the sidebar.
function Sidebar() {
const { currentUser } = useAuth()
if (!currentUser) return null
// ...
}
Within your App component, just render the Sidebar component outside of the Switch:
function App() {
return (
<div className="App">
<Router>
<AuthProvider>
<Sidebar />
<Routes />
</AuthProvider>
</Router>
</div>
);
}
function Routes() {
const { currentUser } = useAuth()
return (
<Switch>
{currentUser && <PrivateRoutes />}
<PublicRoutes />
</Switch>
)
}
Basically all you need to do is render the sidebar on all routes. If you need to render custom Sidebar content based off of routes, you can add another Switch within Sidebar. You can add as many Switch components as you want as long as they are within your Router.
Even though i understand what your trying to do, i don't think you should mind put the sidebar inside the component.
React is powerfull enough to cache a lots of stuffs and disable unnecessary renders. I think the path you should go its figure out how to use wisely useCallback useMemo, memo and make all the tricks to prevent re-renders inside the sidebar components. This way you can reuse the sidebarcomponent, or any component, without to think about location.
So I'm trying to use history.push('/path') in my components. My set up looks like this:
App.js
import { Router, Switch, Route } from 'react-router-dom';
// History
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
function App() {
return (
<Router history={history}>
<Switch>
<Route path='/' exact render={(props) => <Homepage />} />
<Route path='/search' render={(props) => <SecondComponent />} />
</Switch>
</Router>
);
}
export default App;
SecondComponent.js
import React from 'react';
import { withRouter } from "react-router-dom";
const SecondComponent = ({ history }) => {
const myFunction = () => {
history.push('/search');
};
return (
<button onClick={() => myFunction}>stuff</button>
);
}
export default withRouter(SecondComponent);
The problem is that when I click the button the URL changes but the component doesn't render. The thing is if I replace Router in app.js with BrowserRouter it works fine but I get a warning: <BrowserRouter> ignores the history prop. To use a custom history, use import { Router } instead of import { BrowserRouter as Router }.
react-router 5x is compatible with history 4x. Check the history version, and if higher than 4x, try downgrading to 4x version, and it should work fine with Router
I'm trying to build a simple example project where the user is redirected to the 'contact' page upon clicking a button, using React. I'm trying to achieve this by setting the value of a state property. When I run the code I have, it does change the browser address bar URL to that of the contact page, but does not seem to actually load the component - I get a blank page instead. If I manually navigate to that URL (http://localhost:3000/contact) I can see the contents.
Here are my App.js and Contact.js files -
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import Contact from './Contact';
class App extends Component {
state = {
redirect: false
}
setRedirect = () => {
this.setState({
redirect: true
})
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/contact' />
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/contact' component={Contact} />
</Switch>
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
</Router>
)
}
}
export default App;
Contact.js
import React, { Component } from 'react';
class Contact extends Component {
render() {
return (
<div>
<h2>Contact Me</h2>
<input type="text"></input>
</div>
);
}
}
export default Contact;
Using state isn't really a requirement for me, so other (preferably simpler) methods of redirection would be appreciated too.
Since your button is nothing more than a link, you could replace it with:
<Link to="/contact">Redirect</Link>
There are many alternatives though, you could for example look into BrowserRouter's browserHistory:
import { browserHistory } from 'react-router'
browserHistory.push("/contact")
Or perhaps this.props.history.push("/contact").
There are pros and cons to every method, you'll have to look into each and see which you prefer.
I got here for a similiar situation. It's possible use withRouter (https://reactrouter.com/web/api/withRouter) to handle that.
This example was tested with "react": "^16.13.1","react-router-dom": "^5.2.0" and "history": "^5.0.0" into "dependecies" sections in package.json file.
In App.js I have the BrowserRouter (usually people import BrowserRouter as Router, I prefer work with original names) with Home and Contact.
App.js
import React, { Component } from "react";
import {
BrowserRouter,
Switch,
Route,
} from "react-router-dom";
import Home from "./pages/Home";
import Contact from "./pages/Contact";
class App extends Component
{
// stuff...
render()
{
return (
<BrowserRouter>
<Switch>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
);
}
}
export default App;
ASIDE 1: The Route with path="/contact" is placed before path="/" because Switch render the first match, so put Home at the end. If you have path="/something" and path="/something/:id" place the more specific route (with /:id in this case) before. (https://reactrouter.com/web/api/Switch)
ASIDE 2: I'm using class component but I believe (I didn't test it) a functional component will also work.
In Home.js and Contact.js I use withRouter associated with export keyword. This makes Home and Contact components receive the history object of BrowserRouter via props. Use method push() to add "/contact" and "/" to the history stack. (https://reactrouter.com/web/api/history).
Home.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Home = ( props ) =>
{
return (
<div>
Home!
<button
onClick={ () => props.history.push( "/contact" ) }
>
Get in Touch
<button>
</div>
);
}
export default withRouter( Home );
Contact.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<button
onClick={ () => props.history.push( "/" ) }
>
Go Home
<button>
</div>
);
}
export default withRouter( Contact );
Particularly, I'm using also in a BackButton component with goBack() to navigate backwards:
BackButton.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const BackButton = ( props ) =>
{
return (
<button
onClick={ () => props.history.goBack() }
>
Go back
<button>
);
}
export default withRouter( BackButton );
So I could modify the Contact to:
Contact.js (with BackButton)
import React from "react";
import BackButton from "../components/BackButton";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<BackButton />
</div>
);
}
export default Contact; // now I'm not using history in this file.
// the navigation responsability is inside BackButton component.
Above was the best solution for me. Other possible solutions are:
useHistory Hook (https://reactrouter.com/web/api/Hooks)
work with Router instead BrowserRouter - (https://reactrouter.com/web/api/Router)