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

Disallow useLoaderData in errorElement #9735

Merged
merged 7 commits into from Dec 16, 2022

Conversation

brophdawg11
Copy link
Contributor

Matches Remix behavior since loaderData is not reliable in errorElements since we haven't revalidated the data

@changeset-bot
Copy link

changeset-bot bot commented Dec 15, 2022

🦋 Changeset detected

Latest commit: cf23c80

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@remix-run/router Patch
react-router Patch
react-router-dom Patch
react-router-dom-v5-compat Patch
react-router-native Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

value={this.state.error}
children={this.props.component}
/>
</RouteContext.Provider>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to put the error boundary inside the RouteContext so it reflects the right matches, otherwise it thinks it' at the parent layer in the nested hierarchy 😬

Comment on lines +680 to +695
function useRouteContext(hookName: DataRouterStateHook) {
let route = React.useContext(RouteContext);
invariant(route, getDataRouterConsoleError(hookName));
return route;
}

function useCurrentRouteId(hookName: DataRouterStateHook) {
let route = useRouteContext(hookName);
let thisRoute = route.matches[route.matches.length - 1];
invariant(
thisRoute.route.id,
`${hookName} can only be used on routes that contain a unique "id"`
);
return thisRoute.route.id;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private utility hooks

Comment on lines +752 to +758
if (state.errors && state.errors[routeId] != null) {
console.error(
`You cannot \`useLoaderData\` in an errorElement (routeId: ${routeId})`
);
return undefined;
}
return state.loaderData[routeId];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brought over from Remix - if we're calling this from the currently rendered error boundary match, return undefined

@@ -2306,6 +2306,44 @@ describe("a router", () => {
child: new Error("Kaboom!"),
});
});

it("clears previous loaderData at that route", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a handful of new tests because while in here I found that in some cases the router wasn't properly cleaning up stale actionData/loaderData upon a subsequent error

@@ -1142,10 +1151,10 @@ export function createRouter(init: RouterInit): Router {
if (matchesToLoad.length === 0 && revalidatingFetchers.length === 0) {
completeNavigation(location, {
matches,
loaderData: mergeLoaderData(state.loaderData, {}, matches),
loaderData: {},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a duplicative call, we can just let the empty object be merged inside completeNavigation now

@@ -2535,7 +2550,7 @@ function getMatchesToLoad(
? Object.values(pendingError)[0]
: pendingActionData
? Object.values(pendingActionData)[0]
: null;
: undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to undefined to ensure it properly reflects "no action data" as compared to an action that returned null

@@ -2899,6 +2914,9 @@ function processRouteLoaderData(
errors[boundaryMatch.route.id] = error;
}

// Clear our any prior loaderData for the throwing route
loaderData[id] = undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this loader threw an error - we mark this to ensure that we don't hang onto it's prior loaderData during the merge

if (pendingError) {
errors = pendingError;
loaderData[Object.keys(pendingError)[0]] = undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a route threw in an action, we wouldn't have run it's loaders so we don't hit it in the results loop above, so also ensure we clear out it's prior loaderData

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants