Search code examples
reactjsreact-router-dom

How to save previous location in React Router after Redirect


I have a ReactTable component which accepts an onRowClick callback function as a property.

I use this component to list an array of Purchase objects. On click of any Purchase (row), I want the user to be redirected from /purchases/ to /purchases/:order_number. I am currently doing this by setting a redirect state to the order number, and conditionally rendering a Redirect compoennt:

const Purchases:React.FC<> = ({}) => {
   const Purchases: Array<Purchase> = useSelector((state: RootState) => state.shop.purchases)
   const [redirect, setRedirect] = useState<string | null>(null)

   return (
     <Fragment>
       <TableContainer
          total={null}
          data={Purchases}
          hide={[]}
          onRowClick={(row, idx) => {
            setRedirect("/purchase/" + row["Order Number"])
          }}
        />
       {redirect !== null ? <Redirect to={redirect} /> : null}
     </Fragment>
  )
}

The problem is, after I go to /purchase/:order_number, I cannot go back to the previous page because Redirect does not push to history, it just sets the current route to the next (the one specified in the to prop). Is there any way to solve this properly?

The version of React Router Dom I am using is 5.2.0 which does NOT support the Navigate component mentioned in the answer on the question marked as duplicate.

After implementing the solution provided in this question and trying some of the answers from the "duplicate" marked question, I discovered that these solutions actually do NOT WORK!!!

If the user is redirected from routeA to routeB and uses a NavLink to go to routeC, after clicking the Back button, instead of going back to routeB, the user is taken to routeA. This also happens even if the user goes from routeB to routeC and then to routeD....


Solution

  • The way to solve this is to somehow store the current location, or find a way to pass it to the next one via the location property.

    You can simply use local storage, but I'd suggest passing the location in the state via the Redirect component:

    const location: string = useLocation<{ pathname: string }>().pathname
    
    <Redirect to={{
      pathname: "/purchase/12345",
      state: { prevLocation: location }
    }} />
    

    Then in the component which is rendered on the route /purhcase/:order_number, you'd use the useHistory hook provided by react-router-dom, accept the location property and have a useEffect and an event listener which listens for the popstate event, and run history.push with the proper location whenever the popstate event is triggered:

    const PurchasePage: React.FC<{ location: { state: { prevLocation: string } } }> = ({ location }) => {
      const history = useHistory()
    
      useEffect(() => {
        window.addEventListener('popstate', () => history.push(location.state.prevLocation))
        return () => window.removeEventListener('popstate', () => history.push(location.state.prevLocation))
      }, [history])
    
      return <SomeJsxComponent />
    }
    

    This way, you pass the location via props to the next component which is rendered after the Redirect and intercept the back button in order to push to history the previous location.