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

fix(react-router): make sure full matches are passed into route functions #3039

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions packages/react-router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,16 @@ export class Router<

const matches: Array<AnyRouteMatch> = []

const getParentContext = (parentMatch?: AnyRouteMatch) => {
const parentMatchId = parentMatch?.id

const parentContext = !parentMatchId
? ((this.options.context as any) ?? {})
: (parentMatch.context ?? this.options.context ?? {})

return parentContext
}

matchedRoutes.forEach((route, index) => {
// Take each matched route and resolve + validate its search params
// This has to happen serially because each route's search params
Expand Down Expand Up @@ -1361,17 +1371,6 @@ export class Router<
}
}

const headFnContent = route.options.head?.({
matches,
match,
params: match.params,
loaderData: match.loaderData ?? undefined,
})

match.links = headFnContent?.links
match.scripts = headFnContent?.scripts
match.meta = headFnContent?.meta

// If it's already a success, update the headers
// These may get updated again if the match is refreshed
// due to being stale
Expand All @@ -1389,25 +1388,31 @@ export class Router<
// update the searchError if there is one
match.searchError = searchError

const parentMatchId = parentMatch?.id

const parentContext = !parentMatchId
? ((this.options.context as any) ?? {})
: (parentMatch.context ?? this.options.context ?? {})
const parentContext = getParentContext(parentMatch)

match.context = {
...parentContext,
...match.__routeContext,
...match.__beforeLoadContext,
}

matches.push(match)
})

matches.forEach((match, index) => {
const route = this.looseRoutesById[match.routeId]!
const existingMatch = this.getMatch(match.id)

// only execute `context` if we are not just building a location
if (!existingMatch && opts?._buildLocation !== true) {
const parentMatch = matches[index - 1]
const parentContext = getParentContext(parentMatch)

// Update the match's context
const contextFnContext: RouteContextOptions<any, any, any, any> = {
deps: loaderDeps,
deps: match.loaderDeps,
params: match.params,
context: match.context,
context: parentContext,
location: next,
navigate: (opts: any) =>
this.navigate({ ...opts, _fromLocation: next }),
Expand All @@ -1428,10 +1433,19 @@ export class Router<
}
}

matches.push(match)
const headFnContent = route.options.head?.({
matches,
match,
params: match.params,
loaderData: match.loaderData ?? undefined,
})

match.links = headFnContent?.links
match.scripts = headFnContent?.scripts
match.meta = headFnContent?.meta
})

return matches as any
return matches
}

getMatchedRoutes = (next: ParsedLocation, dest?: BuildNextOptions) => {
Expand Down
Loading