-
Notifications
You must be signed in to change notification settings - Fork 55
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
How to resolve getData in sequence #163
Comments
Are you using some central store like Redux? The way you would do this is to have some sort of app-level cache or store. In the child This is easiest if you have some sort of primitive that's like "retrieve this piece of data if it's not already in the cache or has a pending request". But it is guaranteed that your |
Yes, I am using redux and I've thought about this, but this seems a bit dirty, because this basically means that I have to implement the pending state myself and copy it across every component. For example, in every I could probably make a PR for this if I'll have enough time |
We thought about it a bit, but we ultimately didn't end up with cases where that was necessary, especially because we mostly use Relay, which is less friendly to that sort of pattern. It's a bit of a trade-off either way, I guess. If you explicitly state dependencies in If it were just a single async check for e.g. authorization, you could do something like: class AuthorizedDataRoute extends Route {
constructor({ getDataWithAuthorization, ...props }) {
super({
...props,
getData: (match) => (
getAuthorizationData(match).then(authorizationData => (
getDataWithAuthorization(match, authorizationData)
)
),
});
}
} If you want something quick and dirty, and you have caching, you could also just loop through your route config object and recursively set up function setDeferredGetData(routeConfig, pathGetData = () => {}) {
for (const route of routeConfig) {
if (!route.deferred && !route.children) {
continue;
}
const originalGetData = route.getData;
let routeGetData;
if (originalGetData) {
routeGetData = (match) => (
Promise.resolve(pathGetData(match)).then(
() => originalGetData.call(route, match),
)
);
} else {
routeGetData = pathGetData;
}
if (route.deferred) {
route.getData = routeGetData;
}
if (route.children) {
setDeferredGetData(route.children, routeGetData);
}
}
} |
I already figured out a small hack which I can use to solve the issue, but I think that other people may also meet this problem and I would like to add an option to the library which would defer the route's promises automatically. The API would be simple - user will provide a boolean If you agree with this proposal, it would be nice if you could point me in a direction of the best way to implement it, since I'm not familiar with found's codebase. |
Just so I understand, the goal here is that you don't want the nested That sounds like a reasonable use case. You'd want to start with Lines 17 to 21 in b9a57cd
getRouteValues with something more specific that maybe builds an array of the parent getData promises, and adds a Promise.all before calling any child getData bits.
Let me know how that sounds. I can pull out that traversal helper in the next few days. |
Yes, exactly, but also in some cases I have some explicit data dependencies which I will have to load for all nested routes beforehand, but the API would be the same anyway, so it doesn't matter too much. Making a function which builds a promise sequence depending on routes sounds reasonable for me, so as soon as you will roll out the helper I will be able to start working on it |
@taion I've looked at the found's code and gathered an understanding of how to implement this. The helper that you've mentioned would be really useful, so could you tell me when could you roll it out? |
Soon! Sorry, it’s been really busy this week. |
No problem, just let me now when it's ready, I will be trying to make some tests and prototype implementations for now |
I've come up with this small fix. Since getRouteValues iterates over routes in order of depth, I save the parallel promises into the This approach prevents the With these modifications all the current tests pass and it works great in my project. What do you think?
|
That looks about right. I'll get the helper up soon. |
It'd be a separate |
It's not a problem, I could make a PR with this change any time and maybe a couple of tests if needed |
See #165. We may want to make this a different resolver, though, since this will require creating temporary objects. I guess alternatively we can scan through the matches and check if any route has |
Hm, I'm not sure if it needs to be a separate resolver, because by default it doesn't change the current behaviour in any way and a separate resolver wouldn't have any differences with the original besides the getRouteValues replacement, so basically it would be a copypaste. But it doesn't matter for me, just tell me which way should I do it and I will make a PR in a couple of days |
Okay, let's just add it here, then. |
If you have a chance, though, could you let me know whether #165 looks right to you? I might cut a quick release for that just to unblock my long-standing Found Relay issue. |
Looks fine to me, but I did not understand entirely - should I use |
You'd need to do both. The reason behind For example, the route tree might look like:
As a list, we might represent this as |
Got it. There is one more question though - which tests do I have to make (if any) for the deferred routes? |
I don't think I have any test coverage at all for this resolver \: If you can add some basic test coverage, great. If not, I'm fine without additional test coverage for now, assuming you can check that it does in fact work for you. |
@taion Hi again! It's been a long time since I've had my hands on this feature, but now I finally have the time to finish it and after some struggling I have come up with a function, which works as we have discussed. However, I'm not confident if my solution is right, because it feels a bit sketchy, therefore, I post the code of the function here before opening a PR and would really appreciate to hear your comments on this.
This function replaces |
Sorry for the delayed response. That's mostly right. I don't quite understand why you're storing |
Because I need to save the promise in the |
Why isn't the |
Did not quite understand you on this. Sufficient for what? If you were asking why it's not sufficient for building the promise chain, then the answer is because If the question is why not use the value as the id, then you can see, that the value is an object, and I'm not quite sure how it would work as the key in object or a |
It will call for:
So even for ancestors it will only run once. |
Of course, but currently I call it for each match inside the First reason is that accumulateRouteValues expects an array of routeIndices, which is accessible only from an individual match. Since we get an array of matches, I assumed that I need to run it for each match and use their individual routeIndices. And the second reason is that I don't quite understand how we would then map accumulated values back to the matches array in your example from the last message. Would the order of named route values be always the same as in routeMatches array? |
oh, shoot – i missed that. but, yeah, you can just use and the other guarantee that |
by
i mean specifically that what i have above works... it's just that the API is slightly awkward since you have this array where you're only looking at the first value... but in this case i think that's fine. at least, i think a comment explaining something like " |
Alright, I'll try to rewrite this piece in the next few days then and report it back here. |
thanks! feel free to put anything you have in a PR – i think it's easier to use PR line comments anyway |
Got it, next time I'll come back with a PR then ;) |
Hi. I'm having a trouble trying to figure out how to make my data request to fire in sequence.
In particular, I have a following config
I would like my root
getData
to fetch the authorization status and make the nested routes wait until the promise is resolved. Then, after auth is resolved, I want to check my authorization status, and if the user is logged in /pages/:id route will load the page's data. And eventually, if Page's getData didn't throw a 404 error, /pages/:id/settings promise should fire and load the data depending on the page's type.Now all promises fire at once and don't wait until either of them finishes, and as far as I understand this is an intended logic. However, I need a way to make nested getData calls to be pending until upper-level
getData
promises are resolved, and in case if upper-level promises throw a redirect or 404 error I don't want lower-levelgetData
to fire at all.Is this possible with found?
Thanks
The text was updated successfully, but these errors were encountered: