Search code examples
react-routerreact.rb

Using React Router and React.rb


I am trying to use React Router from within react.rb. I started using the reactor-router Gem, but the Gem only works with React Router < 1 and I am using React Router 2.4.0 and the API is quite different.

Over the last weeks I have taken a few approaches to getting this working but none of the approaches have been correct, each having their own fault.

Please will someone steer me in the right direction as I am all out of options.

In terms of setup, I am using Webpack to require React and React Router so the application.js which is injected by Webpack looks like this:

React = require('react')
ReactDOM = require('react-dom')
_reactRouter = require('react-router')

Approach 1 - create the Router as native JS and call ReactDOM.render to render the router when rendering a top level component

before_mount do

  @admin_members = React::API::create_native_react_class(Components::Company::AdminMember)
  @squad_index = React::API::create_native_react_class(Components::Squad::Index)
  @squad_show = React::API::create_native_react_class(Components::Squad::Show)
  @tribe_index = React::API::create_native_react_class(Components::Tribe::Index)
  @tribe_show = React::API::create_native_react_class(Components::Tribe::Show)

end

and then rendering the router to a div in after_mount:

after_mount do
   `ReactDOM.render(React.createElement(
   _reactRouter.Router,
   { history: _reactRouter.browserHistory },
   React.createElement(_reactRouter.Route, { path: "admin/members", component: #{@admin_members} }),
   React.createElement(_reactRouter.Route, { path: "/squads", component: #{@squad_index} }),
   React.createElement(_reactRouter.Route, { path: "/squads/:squad_id", component: #{@squad_show} }),
   React.createElement(_reactRouter.Route, { path: "/tribes", component: #{@tribe_index} }),
   React.createElement(_reactRouter.Route, { path: "/tribes/:tribe_id", component: #{@tribe_show} })
      ), document.getElementById('bh_router_div')
   );`
end

This approach, although not pretty, seems to work in that the Router is created and functions as expected. A URL or /tribe/22 will load the correct TribeShow component and will pass the correct ID to the component.

The problem I have with this approach is when it comes to creating a Link as the Link component does not share the same context as the router. I believe this is down to ReactDOM.render being called once by react-rb and then once again in the code above. This creates two root components on the page (TopLevelRailsComponent) and (ReactRouter).

The Link is created thus:

class MyRouter < React::NativeLibrary
  imports '_reactRouter'
end

And then used in a components render method like this:

MyRouter.Link({to: "/admin/members"}) { "and here is the link"}

The link is rendered, but clicking on it gives the following warning and does not navigate to the component:

Uncaught TypeError: Cannot read property 'push' of undefined

Looking at the properties of the Link Component I see that context is null and I believe this is why. It seems the Link is being being drawn outside the context of the router.

Approach 2 - use the react-rb API to render the router so that ReactDOM.render is not being called twice on the page

This seems to be a better approach but so far I have not managed to get this to work properly.

Building on how I create the Link above, in the render method of a component:

MyRouter.Router({history: `_reactRouter.browserHistory` },
        MyRouter.Route({ path: "/admin/members", component: @admin_members})
) {}

But I get the following warning and the page does not render:

Uncaught Invariant Violation: <Route> elements are for router configuration only and should not be rendered

Approach 3 - build the Route component in native JS so that it does not get rendered:

`var AppRoutes = React.createElement(_reactRouter.Route, { path: "/admin/members", component: #{@admin_members} });`

 MyRouter.Router({history: `_reactRouter.browserHistory` },
        `AppRoutes`
 ) {}

This gets past the previous error, BUT the router does not actually route and I get the following warning (and the component does not render):

Warning: [react-router] Location "/admin/members" did not match any routes

history: As a side note, in both the examples above, I have tried to set history as such:

MyRouter.Router({history: MyRouter.browserHistroy },
  `AppRoutes`
) {}

But I get a warning about providing a depreciated history and when I check the value it is null. Using _reactRouter.browserHistorygets past this warning. I am not sure if this is relevant to this fact that the router is not routing or not.

I would really appreciate any help or steer on this. Even a steer as to which of the approaches is the correct and and any hints as to how to proceed would be very welcome indeed.


Solution

  • This has been addressed in the V2.4.0 branch of reactrb-router https://github.com/reactrb/reactrb-router/tree/v2-4-0

    Also please note the new DSL