And just a suggestion from someone who has been trying to follow this discussion over the last few months, I think it would also be useful for Tom to give a really brief 1-2 minute explanation for exactly why the router is set up the way it is (flat instead of nested) cause it’s something that gets briefly mentioned every now and then but it isn’t something that I’ve ever been able to fully grok.
Slight detour: based on this nested router example, I wonder if behind the scenes there’s really only one tag, <Route> that can take all of these different props, and <Set> and <Private> are just synonyms that let you make the routes easier to read and nest here if you want. But for simplicity in the underlying router code it’s all just calling <Route> and it can, optionally, be nested.
So as far as the router’s “parser” is concerned, the routes could have been defined as:
That’s the way it currently works. The router code loops through all children of <Private> and adds private to them as a prop, and then it removes <Private>. So you just end up with a single list of <Route> components
If I want a context that wraps all my routes, do I add context={MainContext} to <Router> at the top, or do I put a <Set context={MainContext}> just below it that wrapps all other <Set>s and all <Route>s?
I wonder if behind the scenes there’s really only one tag, <Route> that can take all of these different props, and <Set> and <Private> are just synonyms that let you make the routes easier to read and nest here if you want.
@rob Ooh, I like the idea of being able to set the invidual props on the <Route>s or set them in an enclosing <Set>. That is conceptually very simple and gives some flexibility. What happens if both a Set and a Route define a prop? I guess the Route wins, yes? Maybe it’s a warning too, as that could get pretty confusing?
There is nothing really special about Layout or Contexts, right?
@Tobbe Good point! The only requirement is that they need to render their children. In that case, I suggest:
We can also automatically send all these components the URL params, just like we do for Pages, so if they wanted to, they could use that stuff directly. Of course, they could also call useParams and friends.
We need to figure out how these are imported. Pages are auto-imported, which is also how they get code-split. Do we require you to manually import your wrappers? In that case, they will all get included in the main bundle.
This is a good question. I’d want to auto-import them, but that does require us to know where in the filesystem they live. We can look for components named *Layout in /src/layouts/, but what if it’s <Set wrap={[MyLayout, MyAwesomeWrapper]}>, where do we find MyAwesomeWrapper?
It doesn’t have to be all or nothing. We can auto-import what we can, and require the user to manually import the rest. Like we can say “We autoimport all layouts and all contexts, the rest is up to you”. And even for things we have the users import themselves, they can still get code splitting, right? If they just put this at the top of the router file
Going through one of my Redwood apps and I’m overwhelmed by scaffolded routes again and thinking of ways to consolidate. And this is only with three scaffolds! VS Code wrapping them doesn’t help, either:
I haven’t build a Redwood app large enough yet, but my last major Rails app had 38 of them. That’d be 266 routes (!) if each one had to be declared literally! (Redwood equivalent would only be 152 routes.) Rails has the resources shortcut, can we do something similar with <Route>?
<Route path="/admin/users" scaffold="user" />
You’ve still got the path prop so it’s clear what the URL is to get to them and scanning through the routes still works.
You’re always free to use the more verbose route declarations if you want, but for the (probably more common case) of keeping the scaffold as admin pages, it removes a lot of mental overhead.
With this, I could imagine users more likely to keep the generated scaffold code/routes. Otherwise, per your current Routes.js file, it seems people will be inclined to find their own ways to consolidate.
Looks like that could even be done at compile time, right? Using babel.
Only thing that stands out to me is the names. To “expand” to those routes, the scaffold would actually be called “adminUser”, right? And, since the actual page names are never shown anywhere when using your shorthand route, it might be a bit difficult for (new) users to know what name to use when linking directly to e.g. the edit user page, like <Link to={routes.adminEditUser()}>Edit User</Link>
I assume so, but are there drawbacks to that? If we ever released Redwood Router as its own separate package that you can add to any React app, does that make the integration more difficult?
I assumed it could work the same way that <Private> does, where is just adds more attributes to a regular <Route> (although it would also need to inject 3 additional routes, not sure about the feasibility of that).
For sure. We could help by displaying the routes and associated names after you run the scaffold generator (and maybe include a flag if you want the full route from the get-go):
User scaffold generated! To reference your routes by name:
/admin/users - List of all users
routes.adminUsers()
/admin/users/1 - Detail for a single user
routes.adminUser({ id: 1 })
/admin/users/new - New user form
routes.adminNewUser()
/admin/users/1/edit - Edit user form
routes.adminEditUser({ id: 1 })
If you'd rather have the full routes written to your Routes.js
file, use the `--verbose-routes` flag.
We could also include a comment above the shorter <Route> right in the Router file that links to a quick doc about naming conventions.
It ends up being a simple naming convention so once you have it described to you (and throughly documented) I think people will get it. Rails has had this forever and I don’t remember seeing any blow back in the community.
Hah! And here I was thinking "Why isn’t <Private> done with babel as well?!
The main reason to do it with babel would be to not spend cpu cycles doing it (over and over again) at runtime. But yeah, as with everything related to optimizations - should measure first!
React.createElement is the answer to that I think.
Tom, Peter, Rob, and I had a solid discussion about this today and agree it’s ready for moving to implementation of a POC with goal to resolve fully for v1.
Next Steps
Peter is creating a GitHub Issue to outline decisions (from this thread and today’s discussion), plan, and next steps. The conversation should move from here to the Issue when it’s posted.
Tobbe has confirmed he’d like to take lead on implementation in coordination with Peter.
Go Team
And huge thanks to everyone who helped get us this far. This is a critical piece to complete for v1.
@Tobbe Updated the code example in Redwood Router with layouts, context providers, etc to reflect what we talked about in our meeting. It shows two ways to pass props to your Layout, one creating a standalone function and the other doing it inline: