chobo2
chobo2

Reputation: 85725

React Router - How to Determine If Back button was hit?

I have these scenarios

Settings Page -> Results Page -> Details Page

User chooses settings, clicks next, gets results and then clicks into more details.

Details Page -> Results Page

User goes back to Results page from Details Page. This causes a full re-render, causing me to hit the server again for no point(I have the results stored in an array).

Details Page -> Results Page -> Settings Page -> Results Page

The user goes to details, then back to results(want to grab stored results), then goes back to settings page, makes changes and then goes back to results page(now I want a full grab from server again).

I am wondering if there is away in react router to determine if I came to the page via the browser history or if I was going in a forward motion.

Upvotes: 8

Views: 6418

Answers (3)

SUMIT JANGIR
SUMIT JANGIR

Reputation: 1

All the above answers are mostly old. You can use useBlocker() from react-router-dom which will help you in detecting scenarios when you navigate to other page.

Example code for usage:

import {
  useBlocker
} from "react-router-dom";

let blocker = useBlocker(
  ({
    currentLocation,
    nextLocation
  }) =>
  currentLocation.pathname !== nextLocation.pathname && unsavedChanges
);

useEffect(() => {
  if (blocker.state === "blocked") {
    setOpenSaveContentModal(true);
  }
}, [blocker]);

Upvotes: 0

Proustibat
Proustibat

Reputation: 1851

I was looking for the same thing then I finally find a solution that seems simple.

Quick answer:

I use history.action value to determine if the user is coming from a back button or from a classic navigation. If history.action is equal to 'POP' then the back button has been hit. Otherwise it's 'PUSH'.

Detailed answer:

I get access to history in props of each page because I do something like that:

<Provider store = { store }>
    <AppRouter />
</Provider>

Where AppRouter is:

import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import PublicRoute from './PublicRoute';
import PageHome from '../pages/front/home/PageHome';

export const history = createHistory();

const AppRouter = () => (
    <Router history={ history }>
        <div>
            <Switch>
                <PublicRoute
                    exact = { true }
                    path = "/"
                    component = { PageHome }
                    history={ history }
                />
                < ..... Other routes here ..... >
            </Switch>
        </div>
    </Router>
);

export default AppRouter;

And PublicRoute component is something like that:

import React from 'react';
import { Route } from 'react-router-dom';
import Header from '../components/header/Header';

const PublicRoute = ( { component: Component, ...rest } ) => (
    <Route
        component={ ( props ) => (
            <div>
                <Header />
                <Component { ...props } />
            </div>
        )}
        { ...rest }
    />
);

export default PublicRoute;

Upvotes: 9

Gasim
Gasim

Reputation: 7961

In React Router, the component stays mounted if router calls for paths that are children of that component. So, in your example, you can do something like the following:

<Route path="items" component={Results}>
    <Route path=":id" component={Detail} />
</Route>

This way, Results component does not get unmounted when Detail component is being mounted because Detail is a child of Results. However, if you do not want to see Results component getting rendered when you are in Detail, you can only render children when they exist. Something like the following:

 class Results extends React.Component {

     render() {
         if (this.props.children) {
             // This will enter Detail component
             return this.props.children;
         }
         return (
             // Render results component
         );
     }
 }

Upvotes: 2

Related Questions