4

I'm working with a Rails 6 rc1 project with React and Webpacker.

I'm currently using javascript_pack_tag to render React components in my Rails app. I'm trying not to use a gem like react-rails just to keep things simple.

Today I found a weird behavior where my React component would not render after the page is navigated to from a link. However, on a page reload, my component is rendered.

Gemfile:

gem 'rails', '~> 6.0.0.rc1'
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 3.11'
gem 'sass-rails', '~> 5'
gem 'webpacker', '~> 4.0'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'
gem 'bootsnap', '>= 1.4.2', require: false
gem 'devise', '~>4.6'

group :development, :test do
  gem 'pry-byebug'
  gem 'rspec-rails', '~> 3.8'
end

group :development do
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

app/views/layouts/application.html.erb:

<html>
  <head>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
....

app/javascript/packs/application.js:

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

import "bootstrap"
import "../stylesheets/application"

app/views/posts/new.html.erb:

<%= content_tag :div,
  id: 'post_data',
  data: @post do %>
<% end %>
<div id="render_here"></div>
<%= javascript_pack_tag 'new_post' %>

app/javascript/packs/new_post.jsx: (though the React code in this file isn't really relevant for the question)

import React from 'react';
import ReactDOM from 'react-dom';
import PostForm from '../components/PostForm';

document.addEventListener('DOMContentLoaded', () => {
  const node = document.getElementById('post_data');
  const data = JSON.parse(node.getAttribute('data'));
  const dom = document.querySelector('#render_here');

  ReactDOM.render(
    <PostForm post={data} />,
    dom);
});

With this setup, the component is not rendered after clicking on a link that navigates to the page. On page reload, the component is rendered.

I tried a number of things.

In new_post.jsx, after commenting everything out and leaving only console.log(document.readyState), the console displays complete twice. On page load, it displays loading.

Then I tried to use jQuery's document.ready in new_post.jsx:

$(document).ready(function() {
  if (pageInitialized) {
    return;
  }
  pageInitialized = true;

  console.log('render');
...

And again, render is displayed twice after the page is navigated to. On page reload, render is displayed once.

What is going on?

jules
  • 386
  • 4
  • 13
  • Update: I found the culprit to be `require("turbolinks").start()` inside `application.js`. Removing that line makes my component to load correctly. – jules May 11 '19 at 23:03

1 Answers1

3

I know this was resolved for you by a work around, but I came across this question as the first result in my search for the same issue, so for future reference for others having the same problem, I want to leave an explanation.

As described here Rails 5: how to use $(document).ready() with turbo-links a better way would be to listen for the turbolinks events.

document.addEventListener("turbolinks:load", ()=> {
   alert("loaded");
}

or if you need jQuery

$( document ).on("turbolinks:load", ()=> {
    alert("loaded");
}

CoffeScript variant:

document.addEventListener "turbolinks:load",  ->
    alert "loaded"
Canis
  • 4,130
  • 1
  • 23
  • 27
  • 1
    Thanks, this really helps a lot. The workaround @jules mentioned also worked for me, but even better to understand what's going on, and not need to disable turbolinks. – Nicholas Barry Jun 09 '20 at 04:13
  • 1
    Thank you! This really helped me as well. I am using a third-party tables builder in my RoR application, but the tables weren't being built on navigation, only on refresh. Changing `window.addEventListener('DOMContentLoaded', event => {` to `window.addEventListener("turbolinks:load", event => {` fixed the problem. Thank you for your contribution! – CWarrington Jul 26 '21 at 01:37