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.browserHistory
gets 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.