react_on_rails
react_on_rails copied to clipboard
Turbo forms with react_component not rendering React
So I'm using turbo streams for re-rendering forms when there are errors.
where I replace the form
edit.turbo_stream.erb
<%= turbo_stream.replace dom_id(@car), target: '_top' do %>
<%= render partial: 'form' %>
<% end %>
<%= turbo_stream.replace "flashes", partial: "/flash" %>
where form
_form.html.erb
<%= form_with model: @car, id: dom_id(@car), builder: CustomFormBuilder, multipart: true, url: @car.new_record? ? admin_dashboard_cars_path : admin_dashboard_car_path(@car) do |f| %>
...
<%= car_images_upload_input(@car) %>
<%= signal_react_rerender %>
...
where controller actions
def update
@car = Car.find_by(id: params[:id])
@car.assign_attributes(car_params)
@car.release_date = Date.new(car_params[:release_date]&.to_i)
if @car.save
redirect_success
else
form_respond('edit')
end
end
private
def redirect_success
redirect_to admin_dashboard_cars_path(format: :html), notice: I18n.t('flash.update.success')
end
def form_respond(template)
respond_to do |format|
format.html do
flash[:error] = I18n.t('flash.update.error')
render template
end
format.turbo_stream do
flash.now[:error] = I18n.t('flash.update.error')
render template
end
end
end
The issue arises when I submit the form, react_component does not get re-rendered/hydrated even with
ReactOnRails.setOptions({ turbo: true });
The workaround I found is event listening for "turbo:before-stream-render"
const debouncedHandlerForNodes = debounce(() => {
const nodes = document.querySelectorAll("[data-signal-react-rerender]");
if (nodes.length) {
ReactOnRails.reactOnRailsPageLoaded();
nodes.forEach((each) => each.remove());
}
}, 100);
document.addEventListener("turbo:load", turboReloadJs);
document.addEventListener("turbo:before-stream-render", function () {
debouncedHandlerForNodes();
});
turboReloadJs
is just for re-initializing flowbite/tailwindcss
this selector [data-signal-react-rerender]
is searching for span tag from signal_react_rerender
def signal_react_rerender
content_tag(:span, nil, data: { signal_react_rerender: true }, class: 'hidden')
end
Another thing
Something I noticed is without debounced and delayed call to ReactOnRails.reactOnRailsPageLoaded();
console warns with
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.
This is caused because this event gets called twice and of course, it's happening before stream rendering.
Environment
- Ruby version: 3.1.2
- Rails version: 7.0.4
- Webpacker version: 6.5
- React on Rails version: 13.2
Expected behavior
ReactOnRails should re-render/hydrate components after turbo-stream form replacement where we have react_component
helper called within the replaced form
Actual behavior
Components do not get re-rendered/hydrated after turbo-stream form replacement
Small, reproducible repo
https://github.com/uvera/React-on-rails-turbo-js-bug-reproduction
@uvera can we get a small reproducible repo?
@tomdracz any opinions of this? CC: @ahangarha @Judahmeek
@justin808
Here it is https://github.com/uvera/React-on-rails-turbo-js-bug-reproduction
Please check out https://github.com/uvera/React-on-rails-turbo-js-bug-reproduction/blob/main/app/javascript/packs/application-bundle.js
and when running server http://localhost:3000/cars/new
React component does not get rendered after form submission and turbo rerender.
@uvera can you see if #1614 fixes this issue?
See #1620