-
-
Notifications
You must be signed in to change notification settings - Fork 18
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
Proposal: chainRoute
helper
#10
Comments
Use case:
|
We also need chainRoute({
route: editPostRoute,
beforeOpen: fetchPostFx,
// These are optional but I set them to make it more obvious
openOn: fetchPostFx.doneData,
cancelOn: fetchPostFx.failData,
// ===
onCancel: split({
match: error => error.status,
cases: {
404: redirect({ route: notFoundRoute }),
403: redirect({ route: notAllowedRoute })
}
}),
chainedRoute: loadedRoute
}) So here we:
The only problem is that I don't like naming hell here. |
Hi, the proposed solutions feels to me a huge overhead because a lot of new things are introduced. Also, you have to handle edge cases already (adding This is what I feel should be possible with a guarded route:
With suggested solution, it's not clear what to do if I want to show some content (for example 404 page or login page) but keep the url. I would suggest to instead extend route with // 1. Do nothing
// If user is not authenticated, route will not match and nothing will happen
const myRoute = createRoute({ filter: $isAuthenticated });
// 2. Redirect to some other route
// Achieved by creating an inverted route
const myRoute = createRoute({ filter: $canEnter });
const myInvertedRoute = createRoute({ filter: $canEnter.map(v => !v) });
sample({
source: myInvertedRoute.opened,
target: myRoute.open
});
// 3. Show some other content without changing the url
// If user is not authenticated, login route will match and login page shown
const homeRoute = createRoute({ filter: $isAuthenticated });
const loginRoute = createRoute({ filter: isAuthenticated.map(v => !v) });
// 4. Show loading while page data is loaded
// When any of the effects are pending, match `globalLoadingRoute` and show full screen spinner
const getUserFx = createEffect();
const $user = restore(getUserFx.doneData, null);
const mainRoute = createRoute();
const loadedRoute = createRoute({ filter: $user.map(Boolean) });
const globalLoadingRoute = createRoute({ filter: every([geUserFx.pending, ...], true) });
sample({
source: mainRoute.opened,
target: getUserFx,
}); |
@kirillku yeah and you skipped a lot of work that
I agree that |
Also, "just filtering" has another problem. If something tries to open blocked route, it'd be just skipped. |
@Kelin2025 could you please describe what do you want from this feature, like in addition to my points. Seems I have a different concept in my mind. What are the requirements and expected behavior for a gated route? (the proposed approach reminds me |
in a unified way instead of manually handling "should I open subroute" situations etc. I took a look on your filters approach - combined with This thing const mainRoute = createRoute()
sample({
clock: mainRoute.opened,
fn: ({ params: { botId } }) => ({ id: botId }),
target: getBotFx,
})
const botLoadedRoute = createRoute({
filter: every({
predicate: Boolean,
stores: [
mainRoute.$isOpened,
$bot.map(bot => bot && !bot.enabled)
],
}),
}) Will work the same as const mainRoute = createRoute()
const botLoadedRoute = chainRoute({
route: mainRoute,
beforeOpen: getBotFx,
openOn: sample({
clock: getBotFx.doneData,
filter: (bot) => !bot.enabled,
}),
cancelOn: [
getBotFx.failData,
sample({
clock: getBotFx.doneData,
filter: (bot) => bot.enabled,
}),
],
}) The second one does even look more verbose However, the first approach works only with stores. Authorized route example with filters will look like this: const authorizedRoute = <Params>(route: RouteInstance<Params>) => {
guard({
clock: route.opened,
filter: $isNotAuthorized,
target: showLoginFormModal
})
return createRoute({
filter: every({
predicate: Boolean,
stores: [route.$isOpened, $isActuallyAuthorized]
})
})
} IMO this solution
|
Problem
Whenever some route is opened, we often want to do some work before showing actual page
For example,
Yes, you can implement this logic yourself by using basic effector operators.
However, there're two problems.
First-of-all, it's not unified. So each project will implement it differently. Also, we can't build some helpful wrappers around that on the library side
And then, there's lot of boilerplate.
You have to create pending stores, update it, hide page contents, etc.
It might look easy on simple examples. But if you need to do more than a single request, or do some checks like session validation - that's a lot of work
Solution
Introducing
chainRoute
!chainRoute
is a helper, that creates a virtual clone of passed route and listens to passed events before openExample:
What happens here
loadedRoute
route.opened
/route.updated
route.$params
androute.$query
beforeOpen
cancelOn
orroute.left
is triggered during request, we reset stored params/queryopenOn
is triggered, we compareroute.$params/$query
with stored ones. And, if they are equal, we:7.1. Trigger
loadedRoute.open
with stored params7.2. Reset stored params
route.left
is triggered andloadedRoute.$isOpened
is true, we also triggerloadedRoute.left
Notes
So many stuff here! Why not accept just
route
andeffect
?Unfortunately, that won't be flexible.
With just
effect
you cannot solve the following cases:in a declarative way.
So we'll give a bit more low-level control over that, so users can implement any logic they want
What are the benefits over
sample
series?Like I said at the start, there's a lot of boilerplate required for a manual handling
But moreover, if we have consequent requests or custom logic, we need even more extra code:
For example:
With
chainRoute
you can rely on returned route:Some sugar, please!
Sure.
Purpose of this thread
Before I officially publish this solution, I'd like to share it with users, so we could discuss
So yeah, feel free to write your thoughts on that!
The text was updated successfully, but these errors were encountered: