shakacode / react_on_rails

Integration of React + Webpack + Rails + rails/webpacker including server-side rendering of React, enabling a better developer experience and faster client performance.
https://www.shakacode.com/react-on-rails/docs/
MIT License
5.08k stars 627 forks source link

React Component won't mount until all assets are loaded #1601

Closed otaviogaiao closed 3 months ago

otaviogaiao commented 3 months ago

Environment

  1. Ruby version: 3.2.2
  2. Rails version: 7.0.4.3
  3. Shakapacker/Webpacker version: 7.0.2
  4. React on Rails version: 13.4.0

Expected behavior

React component should mount when all javascript and html is loaded.

Actual behavior

If I have image tag referencing an url that its taking a long time to load. The React Component wont show up on the screen until its done or I click on cancel loading button on the browser.

I tried to use


document.addEventListener('DOMContentLoaded', function () {
  ReactOnRails.reactOnRailsPageLoaded();
});

And it makes the component show up, however the following warning appears once the page loads:

react-dom.development.js:86 Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.

But more worrying is that if I start to edit the content on my React Component before the page finishes loading, once it does, it will erase the content, and if in development mode it will show a big alert on the screen with the error:

Hydration failed because the initial UI does not match what was rendered on the server.
There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

I am not using turbo links on this project. I have stimulus in a few components, and my application.js is as follow:


import Rails from '@rails/ujs'
import * as ActiveStorage from '@rails/activestorage'

ActiveStorage.start()
Rails.start();

import $ from 'jquery'
import toastr from 'toastr'
import ReactOnRails from 'react-on-rails';
import { RelacionamentoAutocomplete } from '../components/relacionamento-autocomplete';
import { FormRelatorioModeloOpcao } from '../components/form-relatorio-modelo-opcao';
import { BotaoUploadRelatorio } from '../components/botao-upload-relatorio';

global.$ = $
global.jQuery = $
global.toastr = toastr
global.Rails = Rails

import "controllers"

//tried this just to see if it changed something
ReactOnRails.setOptions({
  turbo: false,
  traceTurbolinks: false
})

ReactOnRails.register({
  RelacionamentoAutocomplete,
  FormRelatorioModeloOpcao,
  BotaoUploadRelatorio
});

import "../view-components";

document.addEventListener('DOMContentLoaded', function () {
  ReactOnRails.reactOnRailsPageLoaded();
});

My javascript is being loaded through:

    <%= javascript_pack_tag 'application', defer: false %>

Here is a print of the errors:

Screenshot 2024-03-06 at 08 37 43 Screenshot 2024-03-06 at 08 37 54

I'm sorry if this is the wrong place, I didn't know where else to go. Looked all over for a solution, but was unable to find it.

Any help is appreciated.

otaviogaiao commented 3 months ago

I did some more investigating. And by looking at the gem code, the component is only rendered when the status of the page is complete. Wouldn't interactive be a more appropriated state to mount the components?

justin808 commented 3 months ago

@otaviogaiao how would know if required assets have loaded? SSR solves the problem of quick rendering.