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