React_on_rails: Can I remount components similar to ReactRailsUJS.mountComponents()

Created on 13 Sep 2016  路  11Comments  路  Source: shakacode/react_on_rails

I have a project which I have converted over from react-rails to react_on_rails and everything is working great for me, except for one issue.

Some of the components which are fetched via ajax and shown in an infinite scroll are re-mounted when they are loaded using the ReactRailsUJS.mountComponents() function. How can I remount the components in this way using react_on_rails or is it not possible or desirable?

question waiting for submitter

All 11 comments

@StevenIseki I have no idea what that mountComponts call does.

Check out:

and see the sibling files in that directory.

I'm quite sure what you want to do is relatively straightforward with the existing version or with a minor PR.

If you really need more help on this, the ShakaCode team is available for hire: http://www.shakacode.com/work/. We'd be happy to help!

Hi @justin808

react-rails has the javascript helper ReactRailsUJS.mountComponents() which can be called to re-mount components. This is used for ajax calls when components are added to the page as shown in this example. You can see how the method is implemented here.

I have found a workaround which gives the same functionality using react_on_rails Calling ReactOnRails.reactOnRailsPageLoaded() remounts the components on the page manually. It works well for me 馃憤

Hey everyone, I was having the same issue and started calling reactOnRailsPageLoaded(), which worked great, thanks!

Recently, a new component was added on the same page, but that component is not re-rendered via ajax. So I have on the same page a component that gets re-rendered via ajax and one that is not. Calling reactOnRailsPageLoaded() on that page throws the following error:

  <Provider> does not support changing `store` on the fly. It is most likely
  that you see this error because you updated to Redux 2.x and React Redux 2.x
  which no longer hot reload reducers automatically.
  See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the
  migration instructions.

That's because I should be re-rendering just the component that gets re-rendered on the ajax call, not both! Checking the mentioned ReactRailsUJS.mountComponents() function, I noticed that it receives a param to scope which components should be mounted again... is that possible on react_on_rails?

Thanks in advance!

@lmatiolis Any chance that you can submit a PR with some docs to /docs/additional-reading/some-good-name.md

This sounds useful, but to be honest, I'm not sure exactly what is the problem that you're solving. Any chance that you can create a tiny example based on the React on Rails generator?

Hey @justin808, thanks for the reply!

Any chance that you can submit a PR with some docs to /docs/additional-reading/some-good-name.md

I don't know what you meant by that... you mean creating a doc about the need to call ReactOnRails.reactOnRailsPageLoaded() when a part of the page is re-rendered via ajax? If it's that, I can certainly start one!

This sounds useful, but to be honest, I'm not sure exactly what is the problem that you're solving. Any chance that you can create a tiny example based on the React on Rails generator?

Yeah, I'll try to create a sample project so you can understand the problem I'm having. I'll find some time to do it Monday!

Thanks again!

@lmatiolis Yes to all your questions! I really appreciate the help, and so does the community!

I really appreciate the help, and so does the community!

And I really appreciate all the great work here on this gem! o/

I'm sorry for the delay (I'm having a busier week than expected here), but I'll still create the test project to explain that issue...

Thanks!

Hey @justin808, finally got some time to create the project with an example (as you can see on the commit referenced). Sorry for the delay...

If you run the project, you won't see the warning I was mentioning because that happens when redux is there too and we are rendering the HelloWorld component like this:

const HelloWorldApp = (props, _railsContext) => {
  const store = createReduxStore(props);

  return (
    <Provider store={store}>
      <HelloWorldContainer />
    </Provider>
  );
};

ReactOnRails.register({ HelloWorldApp });

But I believe the issue I was trying to explain about the need to scope reactOnRailsPageLoaded() load calls to just the DOM element you want is understandable on the example.

Let me know if that helps and is enough or we should add a commit there to setup redux like I mentioned.

Thanks!

Ps: I'll write the wiki page after we make sure we are on the same page on everything here.

@lmatiolis Hi! I commented:

@lmatiolis I'm not getting the point of using UJS with react components. Why not just have the button and component communicate directly?

https://github.com/lmatiolis/react_on_rails_ajax_test_project/commit/c272cc96c253c74ab4d62b4d074d53a031f4dc07

Hi @justin808, I replied there:

Oh, sorry! A better context here would help.

So, on a real example, what's happening is the following: I'm currently on a project that is gradually > transitioning to react. So we are creating new components in react and refactoring old html.erb as we go too... What happened is:

  • We have one react component on our navbar at the top. And some regular rails links that reload content via ajax on the bottom part of the app (it acts like a SPA).
  • We have another react component on the bottom part of the app that ends up being re-rendered depending on user actions (but the navbar is never reloaded via ajax). So that's why the UJS is getting mixed with the react components (I know it's not ideal, but it seems like a fair use since we are doing this gradual transition...)

Pseudo code to better visualize:

<navbar>
  <%= react_component("never gets reloaded on ajax") %>
  <%= button_that_reload_content_below, remote: true %>
</navbar>
<content-that-gets-reloaded-via-navbar-links>
  # A section of the app is shown here according to navbar link.
  <%= react_component("ends up being re-rendered on the ajax call, since all the content on the div > gets reloaded") %>
</content-that-gets-reloaded-via-navbar-links>

Let me know if any of this helps or it's still confusing.

Thanks

lmatiolis/react_on_rails_ajax_test_project@c272cc9

For those finding this issue, there is a different issue here that answered this for me. Our app is a mix of mostly rails + coffeescript / js, and we are sprinkling react in using ReactOnRails as often as we can. We had the situation where a previously coded ajax fetch of code from the server would generate a row in the view, and we needed to add a react component.

Basically you can call ReactOnRails.render("HelloWorldApp", {name: "Stranger"}, 'app');, where:

  • HelloWorldApp == ComponentName
  • { name: 'Stranger' } ==Props`
  • 'app' == ID of component

ReactOnRails creates something like this when you render_component in your view:

<script type="application/json" class="js-react-on-rails-component" data-component-name="ComponentName" data-trace="true" data-dom-id="ComponentName-react-component-51c92ec6-9c16-4ab6-900e-6219183fdae4">{"id":3}</script>
<div id="CustodianNoteButton-react-component-51c92ec6-9c16-4ab6-900e-6219183fdae4"></div>

Using this info, here is what I did:

  • Before my ajax call, get and store all of the elements by $('.js-react-on-rails-component')
  • After ajax call completes, get them all again (storing in a new var)
  • Filter out for just new elements; for example:
// $original_react_components are from before ajax call
// $new_react_components are from after complete and show of new components

var $filtered_components = jQuery.grep($new_react_components, function(n) {
  return jQuery.inArray(n, $original_react_components) == -1;
});

Then loop over the new elements, and get the ComponentName, props, and id from the element itself. Don't forget to JSON.parse the props.

results = [];

for (i = 0, len = $els.length; i < len; i++) {
    el = $els[i];
    component_name = el.getAttribute('data-component-name');
    id = el.getAttribute('data-dom-id');
    props = JSON.parse(el.innerHTML);

    results.push(ReactOnRails.render(component_name, props, id));
}

return results;

This doesn't account for adding new items to your redux store if they don't already exist, but this does get around the issue of triggering all stores and components.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thomassnielsen picture thomassnielsen  路  4Comments

evolve2k picture evolve2k  路  4Comments

CodyFitzpatrick picture CodyFitzpatrick  路  6Comments

cubadomingo picture cubadomingo  路  6Comments

hoenth picture hoenth  路  4Comments