Skip to content
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

4.0 Preview Use Cases #3846

Closed
AndrewIngram opened this issue Sep 14, 2016 · 27 comments
Closed

4.0 Preview Use Cases #3846

AndrewIngram opened this issue Sep 14, 2016 · 27 comments

Comments

@AndrewIngram
Copy link

AndrewIngram commented Sep 14, 2016

The background for this issue is that there are use cases fundamental to real world projects (public websites more so than apps behind an auth check), that I believe aren't supported by the upcoming version 4 of React Router.

These use cases aren't exhaustive, but will hopefully get a productive conversation started. Anyone should feel free to add more to the list. I think there are two possible desirable outcomes here:

  1. We find a way to solve these requirements in RR4 (note, I don't consider defining my routing behaviour twice to be a solution), they get added to the docs and/or examples.
  2. It's concluded that the API can't support every use-case, and the limitations and shortcomings get properly documented (along with the use cases it supports well, obviously :))

Use cases

1. Data-loading

This is the most obvious one, so a good one to start with. Previously, when we used routes, it was possible to map a URL to a list of entry point components. If components were able to statically define their data requirements (a fetch operation for some, a GraphQL fragment for Relay users), it was possible to fetch all the data before rendering the components.

The new API defines matching behaviour in the render method of the components, which means a child "route" can't start loading its data until the parent's render method has been called. Ultimately this means the render process will be staggered and data requests turned into a sequential waterfall. Avoiding this request waterfall is critical to performance.

In previous versions, it was possible for Relay users to fire off all data requests in parallel, or even batch everything up into a single request. This was only possible thanks to being able to statically work out all the data dependencies up front.

Note: I'm not ignoring the value of being able to defer some data dependencies and showing loading messages instead, but I think we'd be going down a tangent if we dwelled on it.

2. Sitemaps, robots and apple-app-site-association files

This is a bundling of a few different requirements that are all basically the same.

Basically, this use case is about the need to have hosted files that explain the structure of the site. At small scales these files can be hard-coded. At larger scales it needs to be possible to generate them (and generate them efficiently). With top-down route definitions, this is relatively easy.

@timdorr
Copy link
Member

timdorr commented Sep 14, 2016

For data loading, the key isn't going to actually be managing loading of data. It's more in the static analysis of routes so you can determine what components are loaded (or need to be loaded) and ask them what data they will need (as static props).

This comes from the loss of a top level route config being the core construct. I know @threepointone is working on something like this, and I'm also thinking about it (haven't had time yet...). While I don't intend to answer the data loading part, I at least want to give a place you can go ask and get the info you need to schedule your fetches appropriately.

@AndrewIngram
Copy link
Author

Yeah, ultimately these are all use cases that are handicapped by the same thing, which is the loss of top-down routing.

@jquense
Copy link
Contributor

jquense commented Sep 14, 2016

cc @taion who maintains the relay integrations as well

@cpsubrian
Copy link

I'm just barely diving into the v4 changes, so take this with a grain of salt, but what about doing a 2-pass render to handle this data batching. This would ideally be abstracted into a module, but something like:

  1. 'route' components use componentWillMount to define loading logic (I'd personally use an HOC provided by an ESNext decorator). Loading config would be gathered up into context or a redux store or wherever.
  2. A 'dummy-renderer' renders your App, essentially throwing out the result (nothing added to DOM).
  3. You run your loading logic
  4. Render your app for real, this time with your fetched data (its now in your redux stores, or wherever)

This could be tweaked to work server-side and client-side.

For route config, I believe the current docs site already has an example of how you might abstract your route config into an object and then dynamically render the tree.

I will say that I'm generally on board with react-router having a much smaller API size and then support modules being built around it to handle various specific use-cases.

@timdorr
Copy link
Member

timdorr commented Sep 14, 2016

@cpsubrian Unfortunately, 2 passes might not be enough. Think about the URL /user/1/invoices/2/payments/3 and how that might be built in a way that needs multiple dependent async requests to resolve. I'm not saying that's a good way to build it, but it could be hard to prevent bad behavior. Relay is definitely an option to avoid that, but I don't think most folks are considering that as an option given all the necessary steps.

@cpsubrian
Copy link

@timdorr Fair enough that it can get tricky. I'd probably have my 'loader api' required the components to abstractly return their data requirements (sorta like graphql) and then the root 'loader' would know how to weave together the various async requests to produce the desired result. That ends up being very app-specific and hard to put in a beginner-friendly example or module.

@cpsubrian
Copy link

My current loading API (redux-based) can handle cascading loader promises, but it works because you can dive into the router components tree and collect all the statically defined loaders.

With the new API you'd actually need to render the tree and collect it up in componentWillMount instead.

The missing piece, as you pointed out, is what if you need actual loaded data in order to complete the render. In my case I'd try to rely only on params from the location but that's not always possible.

@timdorr timdorr changed the title [v4] Use cases for discussion 4.0 - Use cases for discussion Sep 14, 2016
@timdorr timdorr changed the title 4.0 - Use cases for discussion 4.0 Use Cases Sep 14, 2016
@grahammendick
Copy link

Named Routes

If you don't define your routes up front then you can't suppport named routes. There seems to be a consensus among the React Router maintainers that named routes aren't helpful. I can only guess that they've never seen what named routes can do when implemented properly. Named routes are the only way to, for example, build reusable routing components like Pagers and Sorters

@mjackson
Copy link
Member

As @ryanflorence said earlier today, nothing in v4 prevents you from defining your own static route config and using matchPattern to figure out which routes match. Then you can load data and generate your site maps and do whatever else you want to do. The main jobs of react-router are:

  • Help you render stuff based on the current URL
  • Help you navigate around and manage the current URL

It's also extremely important to consider that your data loading requirements are almost certainly not the same as anyone else's. So for us to prescribe an up-front data-loading strategy for everyone to use would be presumptuous. Truth is, there are many, many different strategies and needs when it comes to data loading, and we are more interested in routing here.

Having said that, I do anticipate that we will eventually have some solutions for loading data. But when we do, they will not be part of the core of react-router. They will almost certainly build on top of react-router, but they won't replace it.

@AndrewIngram
Copy link
Author

AndrewIngram commented Sep 14, 2016

(Thanks @grahammendick for mentioning Named Routes, which is something I missed, and is something I find very useful coming from a Django background)

Having to define URL-based logic twice (i.e. the static behaviour in addition to RR's API) doesn't strike me as being much of a solution. The fact that it appears to be necessary in order to use React Router and support the use cases mentioned in this issue seems to be a legitimate argument against using RR4. Now this doesn't mean you absolutely shouldn't use it, just that you need to be aware that the new API is going to create more work for you if you need to do anything similar to these use cases.

The way i'm reading the situation, is that we're leaning towards the 2nd outcome I anticipated: Namely that if you have a heavy need to support these use cases, React Router probably shouldn't be your first choice when it comes to selecting a routing solution. To that end, i'd truly appreciate the limitations being properly documented and made visible in the readme.

I've gone on about this repeatedly in the past, but reducing the duration between a user discovering a library and working out whether it's going fulfill their requirements is the single most important problem that a project's documentation has to solve. Showing off your slick API is super important too, but is ultimately wasted time if the project is unsuitable.

@taion
Copy link
Contributor

taion commented Sep 14, 2016

matchPattern isn't a very good answer to this question, BTW. It's just a thin wrapper around path-to-regexp, and I can install and import path-to-regexp myself just fine. <Match> and others also aren't useful in the case of doing global, up-front routing.

This isn't to say that the RRv4 API isn't a lot cleaner in many ways, but I don't think it's correct to represent RRv4 as offering anything to users who were leaning on routing for data fetching with the RRv2 API.

The RRv4 API doesn't preclude implementing the RRv2 API on top of it, but it also doesn't really offer anything particularly useful in doing so – I have no reason to use matchPattern when I can just use path-to-regexp directly and still have access to things like route reversing.

Again, that's not a problem – it's just not a one-size-fits-all solution.

@mjackson
Copy link
Member

@AndrewIngram Why do we have to advertise all the use cases that we don't handle in our README? We don't tell people we're good for data loading. We never have. React itself doesn't handle data loading. Does it say "React, NOT for data loading"? Of course not.

@timdorr
Copy link
Member

timdorr commented Sep 14, 2016

The way I see it is React Router becomes a smaller thing so that can get out of the way of itself. Meaning, other libraries can use it more freely as a building block to whatever version of routing, data loading, application architecture, etc. that they want to define. Much like Redux doesn't define any architecture to your application's handling of state (only the mechanics of how it's handled), Router shouldn't define anything about how your application should be built.

I think what's available here is a huge opportunity to come in and define what is essentially the "Rails" or "Django" of the React ecosystem. Nothing like that has really taken off yet. But now that Router is less opinionated by design, there is room to apply your own opinions and build a framework of that scale.

Keep in mind this is a prerelease version. This is to give time for the ecosystem to adapt, patterns to develop, libraries to be written, blogs to be posted, screens to be cast, talks to be talked, etc. etc. Now is a great time to start experimenting with these kinds of ideas. We likely need another layer on top of Router to do page-level routing and static route analysis. And there are a number of ways to do that, so the experimenting starts now! 😄

@taion
Copy link
Contributor

taion commented Sep 14, 2016

Certainly, and nobody has had anything but good things to say about how clean the new API is.

Where it's unhelpful is claiming that the exported matchPattern function meaningfully simplifies things for people trying to do route-based async data loading.

It doesn't – and that's okay, not every library has to handle everything – but I think the claim that RRv4 offers useful primitives for building a route-based routing system requires better justification than pointing at 50 lines of code that wrap path-to-regexp.

@mjackson
Copy link
Member

@taion I'm at a workshop today, but I'll write something this weekend/early next week so you can see what I'm talking about. Or, you could dig in and try and write something yourself if you don't wanna wait around for me to do it 😄

@AndrewIngram
Copy link
Author

@mjackson firstly, because URLs are the API for the web, and many people will be coming from a background of expecting to be able to map a URL to a set of data dependencies. Secondly, because whilst the solutions were messy, previous versions of the API nonetheless did have solutions to these problems. Finally, so people stop asking you to support it and can get on with either finding or inventing other solutions.

However, I feel like i'm just answering a rhetorical question... Sure the reasons to clearly document the limits of the scope of a project are obvious enough?

@ryanflorence
Copy link
Member

ryanflorence commented Sep 15, 2016

People keep talking about Relay users. Relay 2 will know what data to load before JavaScript even downloads (they're using static analysis on your code before shipping it).

So ... that means we don't even exist yet. How are we supposed to do anything?

@timdorr
Copy link
Member

timdorr commented Sep 15, 2016

I'm waiting for Relay 3 where it knows what data to download before you even start writing your code.

@cpsubrian
Copy link

@timdorr Haha, if only you were joking. I await my 2nd career as chef after our new artificial intelligence overlords take over the programming industry 😄

@mjackson
Copy link
Member

Re-opening so people can continue this discussion if they wish.

@mjackson mjackson reopened this Sep 15, 2016
@mjackson mjackson changed the title 4.0 Use Cases 4.0 Preview Use Cases Sep 15, 2016
@MrCheater
Copy link

Please see library for fetch data with React-Router v4 https://github.com/MrCheater/react-router-fetch-data (MIT)

@avocadowastaken
Copy link

@MrCheater thank you! Has similar idea yesterday but also with nested matches (mostly for old code migration)

@taion
Copy link
Contributor

taion commented Sep 27, 2016

My current recommendation is to not build on top of RRv4 if you're using Relay. The requirements around static data fetching mean that, in the Relay use case, the RRv2 architecture is a better match to what you're looking for – there's no real value-add from component-based <Match> because you need to run the top-level match anyway.

@peter-mouland
Copy link

Thanks @MrCheater for sharing his route-based solution. I am use to checking if the data exists (maybe it doesn't yet due to server-side timeout or a client-side request) in the container, does the route-based method also handle this?

Has anyone else managed to come up with a component-based solution?

It is the component or container that usually does the data fetching, so it feels like a good idea to carry on with this method. or am I being a bit 'closed-minded' to other options?

@MrCheater
Copy link

@peter-mouland
Standard way (by ReactTraining-team) - https://github.com/reacttraining/react-router-addons-routes , but this way isn't pleasant to me...
Component/container for fetching data is bad idea, because React has synchronous rendering.

@jonricaurte
Copy link

What do you guys think about these libraries for async loading:

https://github.com/ericclemmons/react-resolver
https://github.com/makeomatic/redux-connect

Also if these don't work out, there is also a ui-router port for react which was the defacto solution for angular apps:

https://github.com/ui-router/react

@vjpr
Copy link

vjpr commented Aug 24, 2017

@AndrewIngram Did you find a solution for implementing Sitemaps?

@timdorr If I want to have a sitemap tree view with all my routes is this possible / is there a suggested approach? Have any patterns emerged in the community?

@lock lock bot locked as resolved and limited conversation to collaborators Jan 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests