-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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 usage of Navigate in strict mode when using a data router #10435
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"react-router": patch | ||
--- | ||
|
||
Fix usage of `<Navigate>` in strict mode when using a data router |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,8 +16,10 @@ import { | |
createMemoryHistory, | ||
UNSAFE_invariant as invariant, | ||
parsePath, | ||
resolveTo, | ||
stripBasename, | ||
UNSAFE_warning as warning, | ||
UNSAFE_getPathContributingMatches as getPathContributingMatches, | ||
} from "@remix-run/router"; | ||
|
||
import type { | ||
|
@@ -34,6 +36,7 @@ import { | |
DataRouterContext, | ||
DataRouterStateContext, | ||
AwaitContext, | ||
RouteContext, | ||
} from "./context"; | ||
import { | ||
useAsyncValue, | ||
|
@@ -43,6 +46,7 @@ import { | |
useRoutes, | ||
_renderMatches, | ||
useRoutesImpl, | ||
useLocation, | ||
} from "./hooks"; | ||
|
||
export interface RouterProviderProps { | ||
|
@@ -214,18 +218,24 @@ export function Navigate({ | |
`only ever rendered in response to some user interaction or state change.` | ||
); | ||
|
||
let dataRouterState = React.useContext(DataRouterStateContext); | ||
let { matches } = React.useContext(RouteContext); | ||
let { pathname: locationPathname } = useLocation(); | ||
let navigate = useNavigate(); | ||
|
||
React.useEffect(() => { | ||
// Avoid kicking off multiple navigations if we're in the middle of a | ||
// data-router navigation, since components get re-rendered when we enter | ||
// a submitting/loading state | ||
if (dataRouterState && dataRouterState.navigation.state !== "idle") { | ||
return; | ||
} | ||
navigate(to, { replace, state, relative }); | ||
}); | ||
// Resolve the path outside of the effect so that when effects run twice in | ||
// StrictMode they navigate to the same place | ||
let path = resolveTo( | ||
to, | ||
getPathContributingMatches(matches).map((match) => match.pathnameBase), | ||
locationPathname, | ||
relative === "path" | ||
); | ||
let jsonPath = JSON.stringify(path); | ||
|
||
React.useEffect( | ||
() => navigate(JSON.parse(jsonPath), { replace, state, relative }), | ||
[navigate, jsonPath, relative, replace, state] | ||
); | ||
Comment on lines
+235
to
+238
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason this was problematic in 6.11 is that we would call navigate twice and when in a Instead, we resolve the absolute path here in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little worried about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For future reference we tested the unit test setups from 93ccb2b in demo apps using react 16.8 and 18 and confirmed the UI was the same (even if the underlying React.StrictMode execution approaches differed). We may try to see if we can get our test suite running against both versions in a separate undertaking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I think we're no worse off than we were previously where the effect had no second param since it would have re-run every time anyway? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This wound up biting me. I had an object that I was passing to state that wasn't memo'd. I think the check for dataRouterState !== "idle" saved me prior to 6.11.1, but with this change I had a flurry of redirects (each calling a loader and reloading a file hundreds of times). |
||
|
||
return null; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We no longer need this now that the effect has a proper dependency array - since it won't re-fire on the second render because the path own't have changed.