webpacker-react icon indicating copy to clipboard operation
webpacker-react copied to clipboard

Components don't re-mount after Turbolinks navigation

Open phil-monroe opened this issue 7 years ago • 15 comments

According to this line a react component will only mount if the innerHTML is empty.

// javascript/webpacker_react-npm-module/src/index.js:72
if (node.innerHTML.length === 0) this.render(node, component)

This works well for initial page loads that are not rendered via the Turbolinks cache, but components will not re-mount after navigating around an app that utilize Turbolinks caching. This can be replicated with the following steps:

  1. Use react_component on page one which mount's just fine.
  2. Click link to visit page two. page one's html is cached, the component is unmounted and page two is rendered.
  3. Click the back button, page one is rendered from the Turbolinks cache and the component will not be re-mounted as the components innerHTML is not empty

This is not noticeable for simple components that render static text, but becomes immediately apparent if a component that changes state over time (showing the current time, polling for new data, etc.).

Is there a reason for ensuring innerHTML is empty before mounting a component? Or perhaps the components should be un-mounted on turbolinks:before-cache to avoid being cached all together?

phil-monroe avatar Mar 29 '17 22:03 phil-monroe

Thanks for the report @phil-monroe!

@sevos do you remember why this check was needed?

renchap avatar Mar 30 '17 07:03 renchap

Hi, I'm new to React and I'm trying to render a component in my layout file. I'm not sure if this issue is related to what I'm facing. The components do render when I visit different links but I'm getting the following error every time I visit another link:

unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.

In my layout file, I am rendering a simple component:

/ application.html.slim
/ left out some code...
= javascript_pack_tag 'admin'
= react_component('Header')

Here's in the root pack that I'm calling:

// app/javascript/packs/root_pack.js
import Header from './header/header';
import Turbolinks from 'turbolinks';
Turbolinks.start();

WebpackerReact.setup({Header});

Obviously, the error disappears when I remove turbolinks. I think that it's great having it there, I feel that the page loads sluggishly without it ;)

Appreciate the help!

ghost avatar Apr 10 '17 07:04 ghost

What versions of the gem and npm package are you using? @igikorn

sevos avatar Apr 10 '17 07:04 sevos

@renchap I have no idea why we have this line there. Imho it shouldn't be there, but I may be wrong.

sevos avatar Apr 10 '17 07:04 sevos

@sevos Thanks for the reply!

Yarn

"webpack": "^2.3.3", "webpack-dev-server": "^2.4.2", "webpack-manifest-plugin": "^1.1.0", "webpack-merge": "^4.1.0", "webpacker-react": "^0.2.1"

Gemfile

webpacker (1.1) webpacker-react (0.2.0)

ghost avatar Apr 10 '17 07:04 ghost

Could you please provide a minimum repository reproducing the problem? I will take a second look in the evening

sevos avatar Apr 10 '17 07:04 sevos

Hi @sevos, I've reproduced the issue in a minimum repository:

https://github.com/igikorn/webpacker-react-turbolinks

Thank you very much!

ghost avatar Apr 10 '17 08:04 ghost

Hi @sevos any luck with this? Appreciate the help!

ghost avatar Apr 22 '17 03:04 ghost

@igikorn can you tell me if https://github.com/shakacode/react_on_rails has the same Turbolinks issue?

justin808 avatar Apr 22 '17 08:04 justin808

Hi,

Uncaught Error: Cannot find module "turbolinks"

This app is really helpful. I am facing an issue with turbolinks adding to component.

import Hello from 'components/hello'
import WebpackerReact from 'webpacker-react'
// import Turbolinks from 'turbolinks'

// Turbolinks.start()

WebpackerReact.setup({Hello})

Thanks

iozeey avatar Feb 09 '18 06:02 iozeey

@zeeshan-za-ahmad @renchap I think I can help with this one. I released a new version of react_UJS (The NPM module) that fixes a Turbolinks 5 issue. https://github.com/reactjs/react-rails/pull/868

I've released it as the --pre tag waiting on a JQuery confirmation but this may fix your issue.

@justin808 It's rude to go into other people's repos and pitch your gem, please try to HELP people if you're visiting other projects.

BookOfGreg avatar Feb 09 '18 09:02 BookOfGreg

Hey, I've bumped into this issue as well. For me, I found that the easiest way to handle this is to just let turbolinks know that it shouldn't cache pages that include React components with state by including something like

<% content_for :head do %>
  <meta name="turbolinks-cache-control" content="no-cache">
<% end %>
<%= react_component('Component', data: @data) %>

So this way the navigation still occurss with turbolinks, but it won't display a cached versions of the page (which renders the components Html but isn't handled by React AFAIK) bur rather hit the server and re mount the components just like on first visit.

gafemoyano avatar May 04 '18 17:05 gafemoyano

Any news on this issue? I've tried the "no-cache" approach but it won't work for me. Emptied the cache, still nothing. The component just won't mount after a Turbolinks navigation. 😕

EDIT: seems to be working when I use ReactRailsUJS.detectEvents() (after useContext in my case).

MindRave avatar May 25 '18 13:05 MindRave

You could try disabling Turbolinks Page cache globally, and see if the cache is actually the reason why your react component is not getting mounted.

<meta name="turbolinks-cache-control" content="no-cache">

gafemoyano avatar May 25 '18 16:05 gafemoyano

Someone just needs to port over the detection fixes from React_UJS to this NPM package. https://github.com/reactjs/react-rails/pull/868

BookOfGreg avatar May 26 '18 17:05 BookOfGreg