Data function gets default value of context
Hi! I have the next router config:
const routes = [
{
path: "/sign-in",
component: () => <h1>SignIn</h1>
},
{
path: "/",
component: () => (
<NumberContext.Provider value={777}>
<Outlet />
</NumberContext.Provider>
),
children: [
{
path: "home",
component: Home,
data: homeDataFunction
},
// ...other routes
]
}
];
I want to init all app related contexts only in case authenticated user.
But in data function homeDataFunction I get a default value of context. Looks like because it doesn't see Provider.
Link to repro
It works fine in 2 cases:
- in
Homecomponent itself (viauseContext) - if I wrap the whole router with
Providerbut that's not valid way for me.
Thanks in advance!
The challenge here is data functions need to run in parallel regardless of the nesting so they are hoisted out of the flow since we need to be able to call nested ones before we've "rendered" where the children would be in the flow to avoid waterfalls. In so generally Provider has to be above the <Routes> definition. Given the way this parallelized fetching works I don't see a fix for this. Rather different ways to work around this.
They do still run in order though. One thing that can be leveraged though is that the data functions pass the parent's data function return to the next child, so it is possible to create dependencies hierarchically this way. In your case it seems like the desired behavior is to create a provider only once in the logged in portion of the app. Would it be possible to basically use the routeData and the context mechanism to get the auth data. Ie.. in the data function for "/" do the auth and leverage the fact descendants can access it?
Hi, @ryansolid!
I see your point and it definitely seems that it's impossible to make Data Functions capable of handling data fetching from the service layer with its own business logic inside.
But maybe it is possible to give more lifecycle hooks for the each specific route?
For example, if there was a possibility to have some kind of interceptors for each route that would handle cases of manual redirection before data and script fetching yet to be initialized. It would be possible to avoid usage of the auth service in the context in the first place.
So the resolving of the route would look like Interceptor -> Data Function -> Component Rendering. Interceptor also can provide its data to the data function allowing more flexible data fetching depending on the resolved state of the interceptor.
It might look like:
type InterceptorReturn<T> = {
allowed: boolean;
state: T;
};
type InterceptorFunc = (input: {params, location, navigate, data, state}) => InterceprotReturn;
It appears that inability of data functions to be connected to the applications contexts narrows down their use cases significantly, which is really pity because it is really awesome feature 😄
What do you think about this?
Couldn't the data functions themselves serve as this interceptor? Because there is already a data function to data function communication as parent gets passed to child. Everything is non-blocking which might be the reason it might not seem obvious. But I can picture a parent tagging on a signal/resource that is like isAuthorized and then the child can depend on that before doing its data fetching if necessary. The idea is that wiring up this sort of behavior should all be possible with this model, just might take some doing. If we block then we take the ability out of developers hands, whereas non-blocking by using resources/signals we can create the blocking behavior as desired.
I did not mean to bring functionality that will restrict developers in their ways to organize application architecture.
The problem I see is that Data Functions are not context aware, but data for the specific page as well as the page itself usually is context aware. If I am only able to initialize contexts on the top level of the application the whole point of the Dependency Injection is gone, because what is the point of having multiple contexts if I can only have them initialized on the top level?
On the other hand I can go with separation that you are suggesting (if I understood you right). Using the same an example with AuthService, I should initialize it on the top level and use it as a context inside Solid Components but in data functions I should have some kind of 'waterfall' context specific for the data functions. This means that I should support two context in the same time which is not great.
My suggestion about Interceptors was about having an ability to put some logic between initialization of the navigation process and triggering already existing logic of parallel data fetching and script loading. Something like an ability to wrap a route with a component that would be context aware.
As you said in the initial answer that we don't have an ability to make Data Functions context aware so I thought that we can have a piece of context aware code before data and script fetching has taken place.
In addition, even though I have an ability to navigate to another page in the data function I have no ability to prevent script loading that has been started already when the data function is executed. This is the reason, why in our project we decided to implement ProtectedRoute as a wrapping component instead of data function.