-
-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
Stabilize useNavigate/useSubmit/fetcher.submit for data routers #10336
Changes from 5 commits
577c106
eee2b74
dc590e2
efaa2cd
39666e5
257914e
dac0b3a
ede8398
b610b41
b28e320
2b2c8b5
375bff1
5095809
b48bc8b
d3ab031
c2ac4ba
35370c5
59182f4
c1e795e
da61877
bf8d169
9f164a6
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 |
---|---|---|
|
@@ -18,6 +18,7 @@ import { | |
useNavigate, | ||
useNavigation, | ||
useResolvedPath, | ||
useRouteId, | ||
unstable_useBlocker as useBlocker, | ||
UNSAFE_DataRouterContext as DataRouterContext, | ||
UNSAFE_DataRouterStateContext as DataRouterStateContext, | ||
|
@@ -168,6 +169,7 @@ export { | |
useResolvedPath, | ||
useRevalidator, | ||
useRouteError, | ||
useRouteId, | ||
useRouteLoaderData, | ||
useRoutes, | ||
} from "react-router"; | ||
|
@@ -205,7 +207,7 @@ declare global { | |
|
||
interface DOMRouterOpts { | ||
basename?: string; | ||
future?: FutureConfig; | ||
future?: Partial<Omit<FutureConfig, "v7_prependBasename">>; | ||
hydrationData?: HydrationState; | ||
window?: Window; | ||
} | ||
|
@@ -216,7 +218,10 @@ export function createBrowserRouter( | |
): RemixRouter { | ||
return createRouter({ | ||
basename: opts?.basename, | ||
future: opts?.future, | ||
future: { | ||
...opts?.future, | ||
v7_prependBasename: true, | ||
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. Always turn this on for RR - it used to be RR doing the prepending, now it's the router |
||
}, | ||
history: createBrowserHistory({ window: opts?.window }), | ||
hydrationData: opts?.hydrationData || parseHydrationData(), | ||
routes, | ||
|
@@ -230,7 +235,10 @@ export function createHashRouter( | |
): RemixRouter { | ||
return createRouter({ | ||
basename: opts?.basename, | ||
future: opts?.future, | ||
future: { | ||
...opts?.future, | ||
v7_prependBasename: true, | ||
}, | ||
history: createHashHistory({ window: opts?.window }), | ||
hydrationData: opts?.hydrationData || parseHydrationData(), | ||
routes, | ||
|
@@ -946,9 +954,13 @@ export function useSubmit(): SubmitFunction { | |
return useSubmitImpl(); | ||
} | ||
|
||
function useSubmitImpl(fetcherKey?: string, routeId?: string): SubmitFunction { | ||
function useSubmitImpl( | ||
fetcherKey?: string, | ||
fetcherRouteId?: string | ||
): SubmitFunction { | ||
let { router } = useDataRouterContext(DataRouterHook.UseSubmitImpl); | ||
let defaultAction = useFormAction(); | ||
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 is what causes |
||
let { basename } = React.useContext(NavigationContext); | ||
let currentRouteId = useRouteId(); | ||
|
||
return React.useCallback( | ||
(target, options = {}) => { | ||
|
@@ -959,31 +971,39 @@ function useSubmitImpl(fetcherKey?: string, routeId?: string): SubmitFunction { | |
); | ||
} | ||
|
||
let { method, encType, formData, url } = getFormSubmissionInfo( | ||
let { action, method, encType, formData } = getFormSubmissionInfo( | ||
target, | ||
defaultAction, | ||
options | ||
options, | ||
basename | ||
); | ||
|
||
let href = url.pathname + url.search; | ||
// Base options shared between fetch() and navigate() | ||
let opts = { | ||
replace: options.replace, | ||
preventScrollReset: options.preventScrollReset, | ||
formData, | ||
formMethod: method as HTMLFormMethod, | ||
formEncType: encType as FormEncType, | ||
}; | ||
|
||
if (fetcherKey) { | ||
invariant(routeId != null, "No routeId available for useFetcher()"); | ||
router.fetch(fetcherKey, routeId, href, opts); | ||
invariant( | ||
fetcherRouteId != null, | ||
"No routeId available for useFetcher()" | ||
); | ||
router.fetch(fetcherKey, fetcherRouteId, action, opts); | ||
} else { | ||
router.navigate(href, opts); | ||
router.navigate(action, { | ||
...opts, | ||
replace: options.replace, | ||
fromRouteId: currentRouteId, | ||
}); | ||
} | ||
}, | ||
[defaultAction, router, fetcherKey, routeId] | ||
[router, basename, fetcherKey, fetcherRouteId, currentRouteId] | ||
); | ||
} | ||
|
||
// TODO: Deprecate entirely in favor of router.resolvePath()? | ||
export function useFormAction( | ||
action?: string, | ||
{ relative }: { relative?: RelativeRoutingType } = {} | ||
|
@@ -1116,7 +1136,7 @@ export function useFetcher<TData = any>(): FetcherWithComponents<TData> { | |
// fetcher is no longer around. | ||
return () => { | ||
if (!router) { | ||
console.warn(`No fetcher available to clean up from useFetcher()`); | ||
console.warn(`No router available to clean up from useFetcher()`); | ||
return; | ||
} | ||
router.deleteFetcher(fetcherKey); | ||
|
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.
A few things going on here:
action:null
if nothing is available, which lets the router handle differing behavior for a missing action on submission navigations via<Form>
<form action>
/<button formaction>
values to contain thebasename
if it exists for non-JS, but since the router will handle prepending thebasename
now we strip it back off on our resolvedaction
basename
(same as<Form action>
)