remix-run / history

Manage session history with JavaScript
MIT License
8.29k stars 959 forks source link

Page is not rendering after history.push('/path') #822

Open alexandre-reis opened 4 years ago

alexandre-reis commented 4 years ago

Hi,

I have a problem using history v. 5 with react-router-dom. When I call function history.push('/cart') it sends me to the correct page, but it doesn't show the content.

If I use history version: "4.10.1", it works perfectly.

Am I doing something wrong ?

This is my history.js file

import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
export default history;

And this is my app.js

import React from 'react';
// import { Router } from 'react-router-dom';
import { Router } from 'react-router';
import { Provider } from 'react-redux';
import { ToastContainer } from 'react-toastify';
import store from './store/index';
import history from './services/history';

import Routes from './routes';
import GlobalStyle from './styles/global';
import Header from './components/Header';

function App() {
    return (
        <Provider store={store}>
            <Router history={history} >
                <Header />
                <Routes />
                <GlobalStyle />
                <ToastContainer autoClose={3000} />
            </Router>
        </Provider>
    );
}

export default App;
ritch commented 4 years ago

I'm running into the same. Here's a self contained example: https://codesandbox.io/embed/crazy-dirac-04nil?fontsize=14&hidenavigation=1&theme=dark

import React from 'react'
import {Router} from 'react-router'
import {Route} from 'react-router-dom'
import {createBrowserHistory} from 'history'

const myHistory = createBrowserHistory()

export default function App() {
  return (
    <Router history={myHistory}>
      <Route
        path="/"
        exact={true}
        render={() => <h1 onClick={() => myHistory.push('/todos')}>Home</h1>}
      />
      <Route path="/todos" exact={true} render={() => <h1>Todos</h1>} />
    </Router>
  )
}

Downgrading this example to history@4.10.1 fixes the issue.

mattfranciswork0 commented 4 years ago

I have the same

I'm running into the same. Here's a self contained example: https://codesandbox.io/embed/crazy-dirac-04nil?fontsize=14&hidenavigation=1&theme=dark

import React from 'react'
import {Router} from 'react-router'
import {Route} from 'react-router-dom'
import {createBrowserHistory} from 'history'

const myHistory = createBrowserHistory()

export default function App() {
  return (
    <Router history={myHistory}>
      <Route
        path="/"
        exact={true}
        render={() => <h1 onClick={() => myHistory.push('/todos')}>Home</h1>}
      />
      <Route path="/todos" exact={true} render={() => <h1>Todos</h1>} />
    </Router>
  )
}

Downgrading this example to history@4.10.1 fixes the issue.

You made my entire day :)

tavareshenrique commented 4 years ago

I have the same, it only works with me if I downgrade to the history@4.10.1 version, if I upgrade to history@5.0.0, the problem reported here will occur and I don’t know how to fix it to use this new version.

thomasmi commented 4 years ago

I am seeing the same issue with v5 using

<Redirect exact from='/' to='/home' />

when I rolled back to history@4.10.1 it works as expected.

keyvan-m-sadeghi commented 4 years ago

Same problem!

dominguesgm commented 4 years ago

Writing this here to document what I found in more detail, not sure if it is redundant or not. I ran into this issue and tried to do a bit of debugging inside the react-router source.

It seems that with history@5.x.x, the history.push and history.replace methods create a wrapper around the usual location object and store the type of action there.

For example,

context.location = {
  pathname,
  search,
  hash,
  state,
  key,
}

Becomes

context.location = {
  action: 'REPLACE',
  location: {
    pathname,
    search,
    hash,
    state,
    key,
  }
}

(found from logging the context in react-router's Switch implementation to console)

Since the react-router@5.x.x's Switch tries to find pathname in context.location, it won't find it because it's now in context.location.location.

I assume this is being tackled in react-router v6, and I don't know if it makes sense for this stage to update v5 to play nicely with this breaking change on history, but if it does I wouldn't mind taking a shot at it.

StringEpsilon commented 4 years ago

FYI, I posted a more detailed overview of all the breaking changes I could find in #811.

dominguesgm commented 4 years ago

@StringEpsilon Thanks for the list, looks like important info for users. I may be missing something but is the change I mentioned listed there already? In case it isn't, I think it would be a good addition, because it breaks a pretty common use-case.

StringEpsilon commented 4 years ago

It's the history.listen() change. You can see more details on most entries in the gist.

mayur-padshala commented 3 years ago

With history 5.x.x, Fails when using

<Router history={history}>
    ...
</Router>

Rendering works nicely when using BrowserRouter.

<BrowserRouter>
    ...
</BrowserRouter>
StringEpsilon commented 3 years ago

@mayur-novus That's because using <BrowserRouter/> will pull in and use history@4, as it's a direct dependency of react-router, unless your bundler swaps that out explicitly.

ruvaleev commented 3 years ago

Writing this here to document what I found in more detail, not sure if it is redundant or not. I ran into this issue and tried to do a bit of debugging inside the react-router source.

It seems that with history@5.x.x, the history.push and history.replace methods create a wrapper around the usual location object and store the type of action there.

For example,

context.location = {
  pathname,
  search,
  hash,
  state,
  key,
}

Becomes

context.location = {
  action: 'REPLACE',
  location: {
    pathname,
    search,
    hash,
    state,
    key,
  }
}

(found from logging the context in react-router's Switch implementation to console)

Since the react-router@5.x.x's Switch tries to find pathname in context.location, it won't find it because it's now in context.location.location.

I assume this is being tackled in react-router v6, and I don't know if it makes sense for this stage to update v5 to play nicely with this breaking change on history, but if it does I wouldn't mind taking a shot at it.

Thank you so much! I was downgrade my 'hisory' from 5.0.0 to 4.1.0 as you mentioned, and now everything works properly )

jvnlwn commented 2 years ago

Alternatively, here's a solution that does not require downgrading history. Though, assuming react-router v6 solves the issue and is released soon, this solution will be short-lived.

Under <BrowserRouter />, wrap your <Route />s with a component that can handle a CustomEvent, executing a specified navigation method of history provided by the useHistory hook. From outside of your component tree, trigger the CustomEvent.

Here's an example:

// polyfill CustomEvent
import CustomEvent from "custom-event"
import { useEffect } from "react"
import { render } from "react-dom"
import { useHistory } from "react-router-dom"

// Dispatch a history event.
const dispatchHistoryEvent = (action, route, state) => {
  const event = new CustomEvent("history", {
    detail: {
      action,
      route,
      state
    }
  })
  document.dispatchEvent(event)
}

// Execute a history event.
const handleHistoryEvent = (history, event) => {
  const { action, route, state } = event.detail
  history[action](route, state)
}

// Hook up history event listener that will execute history events.
const useHistoryEvent = () => {
  const history = useHistory()

  useEffect(() => {
    const handler = e => handleHistoryEvent(history, e)
    document.addEventListener("history", handler)
    return () => document.removeEventListener("history", handler)
  }, [])
}

const App = ()=> {
  useHistoryEvent()
  // ...
}

render(
  <BrowserRouter>
    <App>
      {/* routes */}
    </App>
  </BrowserRouter>,
  document.getElementById("root")
)

// somewhere outside of component tree
dispatchHistoryEvent("push", "/home/", {/* optional state */})

Note that this works for all history navigation methods, though is not namespaced well for the history.go method. My use case is only for push, replace, back, forward.