Skip to content

prerendering warning #555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
catmando opened this issue Jun 21, 2016 · 14 comments
Closed

prerendering warning #555

catmando opened this issue Jun 21, 2016 · 14 comments

Comments

@catmando
Copy link

I have another issue to discuss.


I am getting "Warning: render(): Target node has markup rendered by React, but..." This is due to the script block that contains console error messages being in the surrounding container div generated by react_component.

I got rid of this by the following monkey patch (which simply moves to script block outside of the outer container div, and removes all linefeeds), but was wondering why nobody else has noticed, it - perhaps I am doing something wrong?

class React::Rails::ComponentMount

      alias_method :old_react_component, :react_component
      def react_component(name, props = {}, options = {}, &block)
        options = context_initializer_options(options, name) if options[:prerender]
        props = serialized_props(props, name, controller)
        old_react_component(top_level_name, props, options, &block).gsub("\n","")
          .gsub(/(<script>.*<\/script>)<\/div>$/,'</div>\1').html_safe +
          footers
      end
end
@rmosolgo
Copy link
Member

Thanks for reporting this and digging in to see what it was! Just curious, does this happen all the time, or only when there are one or more messages for console.log?

I don't use server rendering, so I haven't noticed it!

I wonder what's the best way to handle this ... On the one hand, we could bring your patch into the gem and re-shuffle the rendered HTML after it's been turned into a string. On the other hand, it seems like we could have a simpler, more programmatic approach since we have access to the gem internals. So what other way could we organize this ... hmm...

@catmando
Copy link
Author

catmando commented Jun 21, 2016

I would fix it in the gem properly... I just did it that way as a quick test... let me see if I can give you a PR

Oh and I would guess there has to be at least one message.... the problem is I am using reactrb which always puts at least one message in there, so we ALWAYS see it :-)

Its been that way for over a year, just have not gotten around till now to investigate it...

@catmando
Copy link
Author

catmando commented Jun 21, 2016

So I looked at the way this works, and its going to be tough to reorganize things internally:

the prerenderer is what packages this all up as a rendered component followed by a script block.

The redesign would want to look like something where the outer wrapper component tag, and options is passed into the pre-rerender.

But this would then be a breaking change to the pre-rerenderer which I think by design is meant to be an API!

So I hate to say it but my patch may be the only practical way forward.

Do you want me to do a PR that updates the string before its returned?

@rmosolgo
Copy link
Member

The other options I could think of, none of which were very good:

  • Introduce multiple return, where the return is rendered_component, rendered_logging

  • Instead of logging inline, store the responses and yield them at the bottom of the page, something like content_for(:react_rails_console)

  • Pass a logger into server rendering, where server rendering can send its log messages. Then after server rendering returns, get the messages out and render them

    replay_logger = ReplayLogger.new 
    rendered_component = server_render(component, props, logger: replay_logger) 
    container_div = content_tag(:div, rendered_component) 
    if replay_logger.any?
      (container_div + replay_logger.to_script_tag).html_safe
    else 
       container_div 
    end 

@catmando
Copy link
Author

yeah I agree there are solutions which are much better, but they will break the API won't they?

I guess the replay_logger solution would work assuming that if no "logger" is supplied the API works as today, and adds the logging internally like it does now.

Okay I can give this a try but it will be in a week or so...

@ghost
Copy link

ghost commented Jun 24, 2016

I have the same problem: impossible to prerender anymore

@ghost
Copy link

ghost commented Jun 28, 2016

@catmando, @rmosolgo Hey guys!

Do you have an idea for a workaround while waiting for the official fix?
I launch my app tomorrow and I need the pre-render for the SEO 🙏🏼

have a great day

@rmosolgo
Copy link
Member

rmosolgo commented Jun 28, 2016

You can put this code in an initializer, for example config/initializers/react_rails_workaround.rb

# console_replay puts a <script> tag inside the <div data-react-class>...</div>, 
# which causes React to warn that it tried to reuse some HTML but failed. 
# So, capture the output of the `react_component` call, then find that <script> tag 
# and move it _outside_ the <div data-react-class>...</div>. That way, 
# React can successfully reuse the HTML.
class React::Rails::ComponentMount
  alias_method :old_react_component, :react_component
  def react_component(component_name, props = {}, options = {}, &block)
    prerender_output = old_react_component(component_name, props, options, &block)
    prerender_output
      .gsub("\n","")
      .gsub(/(<script>.*<\/script>)<\/div>$/,'</div>\1').html_safe
  end 
end

Does that work for you?

@ghost
Copy link

ghost commented Jun 28, 2016

@rmosolgo Thank you so much!

The warning is always there
image

But now the content is well rendered, before your workaround I had a white page.

@rmosolgo
Copy link
Member

Oh, I may have made an error in adapting catmando's workaround. I've updated my previous post to include .gsub("\n",""), you could try it again!

@ghost
Copy link

ghost commented Jun 28, 2016

@rmosolgo fixed! thank you for taking the time to help me 👍

@ghost ghost mentioned this issue Jul 4, 2016
@ghost
Copy link

ghost commented Jul 20, 2016

hey @rmosolgo, I've used the same workaround on another app, and now the pre-render: true mount the component two times. It's annoying because every component functions (eq. onClick) are also fired two times.

Do you know where it comes from?

@rmosolgo
Copy link
Member

I don't know where it comes from! Could you open a new issue? It should include:

  • The description of the issue (like you gave above)
  • Application.js requires, components.js requires
  • An example Rails view & React component which double-renders

@rmosolgo
Copy link
Member

Addressing this in #691 with couple changes:

  • more specific targeting of the replay script tag (added a class=)
  • handle component tag: option (not only div)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants