Search code examples
reactjsreact-reduxreact-routerbrowser-history

React Router history on browser's back button


I have a home component with a link which loads a display component and in display component i have the same link which loads the display component again.

if user clicks the link many times in Display Component then there will be a lot of router history.i want that when a user clicks the browser back button it should load the home component not all the previous history.

when i use history.replace("/"); in Display component with onClick event then it works only one time back.but again back is resulting the previous history of Display Component

Routes.js

import Home from "./Components/Home"
import Display from "./Components/Display"

<Router>
    <Switch>
        <Route exact path="/">
            <Home />
        </Route>
        <Route path="/:city">
            <Display />
        </Route>        
    </Switch>
</Router>

Home.js

<Link to ={`/${city}`} onClick={()=>{dispatch(fetchWeather(city)); }}>Search</Link>

Display.js

<Link to ={`/${city}`} onClick={()=>{dispatch(fetchWeather(city)); }}>Search</Link>

Solution

  • Depending on the version of react router you are using you can just add the replace prop on your Link component in the Display.js file to not push new states on the history stack and instead update the current one.

    <Link replace to ={`/${city}`} onClick={()=>{dispatch(fetchWeather(city)); }}>Search</Link>
    

    If you're on an older version where this isn't supported what you can do is have a click handler do this for you

    // Display.js file
    
    function Display(props) {
      // whatever code you have for this file
      const handleViewCity = useCallback((event, city) => {
        // prevent the default redirect from happening, were going to manage that ourselves
        event.preventDefault()
    
        dispatch(fetchWeather(city))
        props.history.replace(`/${city}`)
    
      }, [props.history, fetchWeather])
    
      return (
        // your jsx
        // keep the href so you get browser builtin functionality like right click "open in new window"
        <a href={`/${city}`} onClick={(e) => handleViewCity(e, city)}>Search</Link>
      )
    }
    
    export default withRouter(Display)
    

    To visualize what would be happening here think of history as a stack of locations. (this is a simple example - pseudo code)

    history.push('/city1') // ['/home', '/city1']
    history.push('/city2') // ['/home', '/city1', '/city2']
    history.push('/city3') // ['/home', '/city1', '/city2', '/city3']
    

    Pressing the browser back button fires a window popstate event. Pop being the keyword there. When the browser back button is pressed your history then looks like this ['/home', '/city1', '/city2'], which is why you were seeing different cities from the history.

    Instead you want to use replace to achieve the desired effect

    history.replace('/city1') // ['/home', '/city1']
    history.replace('/city2') // ['/home', '/city2']
    history.replace('/city3') // ['/home', '/city3']