Skip to content

Commit b083d63

Browse files
committed
Refactor route context handling: update routeContext structure for improved reactivity in useRoutesImpl, ensuring child routes register correctly.
1 parent 0b203f2 commit b083d63

File tree

4 files changed

+39
-24
lines changed

4 files changed

+39
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
node_modules
22
llms/
3+
.cursor/
34

45
# Output
56
.output

src/lib/core/components/rendered-route.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@
3030
dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id;
3131
}
3232
33-
RouteContext.setWith(() => routeContext);
33+
// Read specific fields to ensure reactivity when they change
34+
RouteContext.setWith(() => ({
35+
outlet: routeContext.outlet,
36+
matches: routeContext.matches,
37+
isDataRoute: routeContext.isDataRoute,
38+
}));
3439
</script>
3540

3641
{@render children?.()}

src/lib/core/components/route.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@
109109
route.children = children;
110110
}
111111
112+
// This done in an effect because in Svelte,
113+
// effects run from innermost to outermost components.
114+
// This ordering allows:
115+
// 1. Child routes to register themselves with their parent first
116+
// 2. Parent routes to collect their children before registering themselves
117+
// 3. The full route tree to be built from bottom-up
118+
//
119+
// The parent component must read its children's routes from within an effect
120+
// to ensure the children have completed registration first.
112121
addRouteToParent(route);
113122
});
114123
</script>

src/lib/core/components/use-routes.svelte.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export function useRoutesImpl(
136136
match: RouteMatch;
137137
unmountRef?: any;
138138
level: number; // depth in the route hierarchy
139-
routeContext: { current: RouteContextObject };
139+
routeContext: RouteContextObject;
140140
}
141141

142142
const mountedComponents: MountedComponent[] = [];
@@ -192,7 +192,7 @@ export function useRoutesImpl(
192192
outlet: Snippet | null,
193193
matchesSlice: RouteMatch[],
194194
index: number,
195-
routeContext: { current: RouteContextObject }
195+
routeContext: RouteContextObject
196196
): {
197197
component: typeof RenderedRoute | typeof RenderErrorBoundary;
198198
props: any;
@@ -223,20 +223,16 @@ export function useRoutesImpl(
223223
const shouldWrap =
224224
dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0);
225225

226-
routeContext.current = {
227-
outlet,
228-
matches: matchesSlice,
229-
isDataRoute: dataRouterState != null,
230-
};
226+
routeContext.outlet = outlet;
227+
routeContext.matches = matchesSlice;
228+
routeContext.isDataRoute = dataRouterState != null;
231229

232230
// return object include routeContext
233231
const commonReturn = {
234232
component: RenderedRoute,
235233
props: {
236234
match,
237-
get routeContext() {
238-
return routeContext.current;
239-
},
235+
routeContext,
240236
children: childrenSnippet,
241237
},
242238
};
@@ -248,11 +244,7 @@ export function useRoutesImpl(
248244
const renderedRouteSnippet = snippet(anchor => {
249245
RenderedRoute(anchor as any, {
250246
match,
251-
routeContext: {
252-
outlet,
253-
matches: matchesSlice,
254-
isDataRoute: dataRouterState != null,
255-
},
247+
routeContext,
256248
children: childrenSnippet,
257249
});
258250
});
@@ -264,9 +256,7 @@ export function useRoutesImpl(
264256
revalidation: dataRouterState!.revalidation,
265257
error,
266258
component: errorElement!,
267-
get routeContext() {
268-
return routeContext.current;
269-
},
259+
routeContext,
270260
children: renderedRouteSnippet,
271261
},
272262
};
@@ -289,7 +279,11 @@ export function useRoutesImpl(
289279
outletSnippet = snippet(anchor => mountMatchAtLevel(index + 1, anchor, level + 1, matches));
290280
}
291281

292-
const routeContext = $state<{ current: RouteContextObject }>({ current: null! });
282+
const routeContext = $state<RouteContextObject>({
283+
outlet: null,
284+
matches: [],
285+
isDataRoute: false,
286+
});
293287

294288
const { component, props } = createRenderedSnippet(
295289
match,
@@ -316,7 +310,11 @@ export function useRoutesImpl(
316310
outletSnippet = snippet(anchor => mountMatchAtLevel(1, anchor, 1, matches));
317311
}
318312

319-
const routeContext = $state<{ current: RouteContextObject }>({ current: null! });
313+
const routeContext = $state<RouteContextObject>({
314+
outlet: null,
315+
matches: [],
316+
isDataRoute: false,
317+
});
320318
const { component, props } = createRenderedSnippet(
321319
matches[0],
322320
outletSnippet,
@@ -378,11 +376,13 @@ export function useRoutesImpl(
378376
// Partial update - refresh parent's outlet to show new child routes
379377
const parentComponent = mountedComponents[firstDifferentLevel - 1];
380378

381-
if (parentComponent.match.route.children?.length) {
382-
// Update parent's outlet with new route matches
383-
parentComponent.routeContext.current.outlet = snippet(anchor =>
379+
if (parentComponent && parentComponent.match.route.children?.length) {
380+
// Update parent's outlet with new route matches (reactively)
381+
parentComponent.routeContext.outlet = snippet(anchor =>
384382
mountMatchAtLevel(firstDifferentLevel, anchor, firstDifferentLevel, currentMatches)
385383
);
384+
// Keep matches in sync for consumers relying on RouteContext.matches
385+
parentComponent.routeContext.matches = currentMatches.slice(0, firstDifferentLevel);
386386
}
387387
}
388388
}

0 commit comments

Comments
 (0)