React_on_rails: Turbolinks not working properly with react-router

Created on 20 Nov 2016  路  7Comments  路  Source: shakacode/react_on_rails

I found this bug earlier when I was messing around with the dummy app. To reproduce:

  • cd to spec/dummy and start the app with foreman start -f Procfile.static
  • Navigate to localhost:3000/react_router
  • Click on "Router First Page"
  • Go back
  • Go forward
  • Go back again

At this point, the first page component will still be visible on the page, even though we're at /react_router. When going back the first time, turbolinks unloads and reloads the page. I would expect this navigation event to be handled by react-router. Is this a bug, or expected behavior?

discussion question

Most helpful comment

I think I can shed some light on this. The way Turbolinks knows that a PopState event is created by itself is by adding a small marker in the history state. That typically looks like this:

> console.log(window.history.state)
{turbolinks: {restorationIdentifier: "9661db46-ae69-494a-9151-9023a5db0aa6"}}

The problem is react-router also uses the history state for the same purpose, and when it's creating a new location it sadly overwrites whatever state that was already in there, so after visiting a page managed by react-router, your history state will look something like this:

> console.log(window.history.state)
{key: 'unbqk4'}

What you want to do is to listen to the 'popstate' events in the browser and to call Turbolinks programmatically whenever the history object doesn't contain the 'turbolinks' property. You can easily do this in your startup file. Here is how I do it:

// client/registration.js
import Turbolinks from 'turbolinks'
Turbolinks.start()

addEventListener('popstate', (o) => {
  if (!window.history.state.turbolinks) {
    Turbolinks.visit(o.pathname)
  }
})

I haven't tested this thoroughly and there may be some edge cases, but in my small test case the back button seemed to work well enough.

PS: If you want to replace history, simply change the action to 'replace':

Turbolinks.visit(o.pathname, {action: 'replace'})

All 7 comments

@jtibbertsma This might be impossible to solve. We'd have to ensure that only turbolinks or react-router listens to the back button.

In terms of your example, you can create /spec/dummy-no-turbolinks so your example does not have it.

We are not using turbolinks at https://www.friendsandguests.com/ right now due to this issue.

CC: @alexfedoseev

And see #611 and #612.

I see. I don't think I'll need to create a new dummy app though, since this doesn't break any of my test cases.

I think I can shed some light on this. The way Turbolinks knows that a PopState event is created by itself is by adding a small marker in the history state. That typically looks like this:

> console.log(window.history.state)
{turbolinks: {restorationIdentifier: "9661db46-ae69-494a-9151-9023a5db0aa6"}}

The problem is react-router also uses the history state for the same purpose, and when it's creating a new location it sadly overwrites whatever state that was already in there, so after visiting a page managed by react-router, your history state will look something like this:

> console.log(window.history.state)
{key: 'unbqk4'}

What you want to do is to listen to the 'popstate' events in the browser and to call Turbolinks programmatically whenever the history object doesn't contain the 'turbolinks' property. You can easily do this in your startup file. Here is how I do it:

// client/registration.js
import Turbolinks from 'turbolinks'
Turbolinks.start()

addEventListener('popstate', (o) => {
  if (!window.history.state.turbolinks) {
    Turbolinks.visit(o.pathname)
  }
})

I haven't tested this thoroughly and there may be some edge cases, but in my small test case the back button seemed to work well enough.

PS: If you want to replace history, simply change the action to 'replace':

Turbolinks.visit(o.pathname, {action: 'replace'})

Closing for now.

Hi,

I'm planning to use react on rails and I'm new with react. Does it mean that I have to turn off turbolinks in my app and replace it with react router?

@volkanunsal
What If i want to use react-router inside a certain view e.g. /audits
To get to this view I use Turbolinks.visit() which is fine. but inside this view i'm using react-router for routing. Now lets say i go to another view /account and from there I press back. how do I trigger Turbolinks.visit() from there back to /audits?

The problem is we are migrating slowly from Trubolinks to react-router and we want to be able to jump between the views through the back button.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

joeczucha picture joeczucha  路  6Comments

stereodenis picture stereodenis  路  9Comments

serodriguez68 picture serodriguez68  路  7Comments

hoenth picture hoenth  路  4Comments

evolve2k picture evolve2k  路  4Comments