-
-
Notifications
You must be signed in to change notification settings - Fork 923
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make the router use components, but only accept view functions #2281
Comments
Conceptually this is great, but I am not sure whether this would work for the paradigm described below: I have a sample book store, that has two routes:
The user starts out on the book-list view, then clicks on a book to go into book-detail view, the transition is: Component provides an init hook to load data when user arrives at a route, function does not. If route takes view function instead, here is the transition: |
Edit: Forgot to wrap the component in a keyed element. Your routes would end up looking something like this: m(m.route, {
"/book-list": () => m(BookList),
"/book-detail/:id": () => m(m.keyed, m(BookDetail, {key: m.route.params.id})),
// ...
}) For the two components here:
The transitions are still relatively similar, just you would need to use the component yourself rather than pass them directly to Mithril. I'm passing the book ID via Your `BookDetail` component would end up looking something like this, once you're all said and done:function BookDetail(vnode) {
var type = "pending"
var abort, book, error
m.request({
url: `/api/book/${vnode.attrs.key}`
config: xhr => { abort = () => xhr.abort() },
}).then(
b => { type = "ready"; book = b },
e => { type = "error"; error = e },
)
return {
onremove: abort,
view: () =>
type === "pending" ? m(Loading) :
type === "error" ? alertError(error) :
renderBook(book),
}
} If m(Async, {
init: onAbort => m.request({
url: `/api/book/${vnode.attrs.key}`
config: xhr => onAbort(() => xhr.abort()),
}),
pending: () => m(Loading),
ready: book => renderBook(book),
error: error => alertError(error),
}) |
Thank you for the detailed explanation, it helps me understand this new proposal. In the example above
I was under the impression that |
Updates
m(m.route)
tom(m.route.init)
Media
This is part 4 of a broader proposal: #2278.
This is the third major step of how Mithril would become a lot more component-driven and less component-oriented. My goal here would be to further encourage component use over definition, by changing
m.route
to be a bunch of components that accept and use view methods rather than components, so it's easier to use and decompose as necessary. More specifically:m.route.link
to be itself a component rather than anoncreate
method. This would make it far more flexible and less of a general hack.m.route.prefix
a simple property. It'd fix the issue of reading (which is sometimes useful), but it'd also simplify our internals.This would continue to use global state, but it'd limit how much is actually used. The routing is partially static, partially dynamic, and this is by design. I wanted to have the flexibility of dynamic routing (React Router was a heavy influence), but while still keeping as much as possible of the simplicity and conciseness of Mithril's existing static router. (If you're lost about what the difference is, React Router's docs go over this in high detail.)
So here's how that'd look.
Global routes
The global route API would be substantially simplified and made a bit more consistent so it's easier to teach. Also, by making it component-driven, it also allows Meiosis users to leverage the same API, enjoying the benefits of its simplicity without having to use
m.mount
.For the API:
This is how you'd define global routes.
I removed the async resolver aspect of it, but replaced it with something that makes transient checks like authorization and payment much easier to accomplish.
This is how you get and set the prefix. What's set here is the default value, but Mithril reads this as a property when generating and parsing the routes.
This is how you get the current route. This simply becomes a property.
This is how you get the current route params. This simply becomes a property.
This is how you set the current route. Returns a promise resolved once the route change has been completed. All options are optional.
This is how you create links.
If you wanted to, say, include some responsive routes, you could build the route object dynamically in the view:
In case you're wondering where that `Media` component came from...
It's a stripped down port of
react-media
.Local routes
This is an optional part of the
m.route
redesign, meant for:It just helps keep the routing system composable, so people can move child routes into separate modules and recompose them at will. Also, in larger CRUD apps, this could do wonders in helping reduce code duplication.
This is passed as the sole argument for route methods, and all of the global
m.route
methods would be available on route contexts, just with all links and such relative to the context itself rather than the global prefix:m.route.init
→childRoute.init
m.route.get
→childRoute.get
m.route.set
→childRoute.set
m.route.link
→childRoute.link
m.route.prefix
→childRoute.prefix
Most of this would just consist of using a
factory(prefix)
, whereprefix
is the initialroute.prefix
, returning a routing instance. The implementation of the rest is easy:m.route = factory("#!")
andchildRoute = factory(inst.prefix + matchedBase)
.Other questions
Since this fully decouples
m.route
from the rendering algorithm or them.mount
functionality as a direct dependency, we could pull this out of the core distribution, or at least consider creating a bundle with it excluded. I suggest we avoid it initially until I confirm if any meaningful size gains arise from this. (I have to redo the router anyways, and I plan to simplify it like I did withm.mount
/m.redraw
in #2280.)The text was updated successfully, but these errors were encountered: