diff --git a/.changeset/normalize-form-method-deprecation.md b/.changeset/normalize-form-method-deprecation.md new file mode 100644 index 00000000000..9e91779965b --- /dev/null +++ b/.changeset/normalize-form-method-deprecation.md @@ -0,0 +1,6 @@ +--- +"@remix-run/dev": minor +"@remix-run/react": minor +--- + +Add deprecation warnings for `v2_normalizeFormMethod` diff --git a/docs/hooks/use-navigation.md b/docs/hooks/use-navigation.md index 620620d5f09..22f915491b0 100644 --- a/docs/hooks/use-navigation.md +++ b/docs/hooks/use-navigation.md @@ -12,14 +12,17 @@ import { useNavigation } from "@remix-run/react"; function SomeComponent() { const navigation = useNavigation(); - navigation.state; - navigation.location; - navigation.formData; - navigation.formAction; - navigation.formMethod; + navigation.state; // "idle" | "submitting" | "loading" + navigation.location; // Location being navigated to + navigation.formData; // formData being submitted + navigation.formAction; // url being submitted to + navigation.formMethod; // "GET" | "POST" | "PATCH" | "PUT" | "DELETE" } ``` +The `useNavigation().formMethod` field is lowercase without the `future.v2_normalizeFormMethod` [Future Flag][api-development-strategy]. This is being normalized to uppercase to align with the `fetch()` behavior in v2, so please upgrade your Remix v1 applications to adopt the uppercase HTTP methods. + For more information and usage, please refer to the [React Router `useNavigation` docs][rr-usenavigation]. [rr-usenavigation]: https://reactrouter.com/hooks/use-navigation +[api-development-strategy]: ../pages/api-development-strategy diff --git a/integration/action-test.ts b/integration/action-test.ts index dee315b2a55..02e3a900933 100644 --- a/integration/action-test.ts +++ b/integration/action-test.ts @@ -20,6 +20,7 @@ test.describe("actions", () => { future: { v2_routeConvention: true, v2_errorBoundary: true, + v2_normalizeFormMethod: true, }, files: { "app/routes/urlencoded.jsx": js` diff --git a/integration/defer-test.ts b/integration/defer-test.ts index edac58789e1..f72e0cbebe6 100644 --- a/integration/defer-test.ts +++ b/integration/defer-test.ts @@ -36,6 +36,7 @@ test.describe("non-aborted", () => { future: { v2_routeConvention: true, v2_errorBoundary: true, + v2_normalizeFormMethod: true, }, files: { "app/components/counter.tsx": js` diff --git a/integration/hmr-test.ts b/integration/hmr-test.ts index bd08171da42..33b6894e22f 100644 --- a/integration/hmr-test.ts +++ b/integration/hmr-test.ts @@ -17,6 +17,7 @@ let fixture = (options: { port: number; appServerPort: number }) => ({ unstable_tailwind: true, v2_routeConvention: true, v2_errorBoundary: true, + v2_normalizeFormMethod: true, }, files: { "package.json": json({ diff --git a/packages/remix-dev/__tests__/create-test.ts b/packages/remix-dev/__tests__/create-test.ts index 2f9eb196b83..c905ad83d09 100644 --- a/packages/remix-dev/__tests__/create-test.ts +++ b/packages/remix-dev/__tests__/create-test.ts @@ -8,7 +8,11 @@ import stripAnsi from "strip-ansi"; import { run } from "../cli/run"; import { server } from "./msw"; -import { errorBoundaryWarning, flatRoutesWarning } from "../config"; +import { + errorBoundaryWarning, + flatRoutesWarning, + formMethodWarning, +} from "../config"; beforeAll(() => server.listen({ onUnhandledRequest: "error" })); afterAll(() => server.close()); @@ -349,6 +353,8 @@ describe("the create command", () => { ]); expect(output.trim()).toBe( errorBoundaryWarning + + "\n" + + formMethodWarning + "\n" + flatRoutesWarning + "\n\n" + diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts index 1bd1f30220e..3749e44f582 100644 --- a/packages/remix-dev/config.ts +++ b/packages/remix-dev/config.ts @@ -410,6 +410,10 @@ export async function readConfig( warnOnce(errorBoundaryWarning, "v2_errorBoundary"); } + if (!appConfig.future?.v2_normalizeFormMethod) { + warnOnce(formMethodWarning, "v2_normalizeFormMethod"); + } + let isCloudflareRuntime = ["cloudflare-pages", "cloudflare-workers"].includes( appConfig.serverBuildTarget ?? "" ); @@ -775,3 +779,9 @@ export const errorBoundaryWarning = "behavior in Remix v1 via the `future.v2_errorBoundary` flag in your " + "`remix.config.js` file. For more information, see " + "https://remix.run/docs/route/error-boundary-v2"; + +export const formMethodWarning = + "⚠️ DEPRECATED: Please enable the `future.v2_normalizeFormMethod` flag to " + + "prepare for the Remix v2 release. Lowercase `useNavigation().formMethod`" + + "values are being normalized to uppercase in v2 to align with the `fetch()` " + + "behavior. For more information, see https://remix.run/docs/hooks/use-navigation"; diff --git a/packages/remix-react/browser.tsx b/packages/remix-react/browser.tsx index 6286c367398..35993a5d4d2 100644 --- a/packages/remix-react/browser.tsx +++ b/packages/remix-react/browser.tsx @@ -151,6 +151,16 @@ export function RemixBrowser(_props: RemixBrowserProps): ReactElement { ); } + if (!window.__remixContext.future.v2_normalizeFormMethod) { + warnOnce( + false, + "⚠️ DEPRECATED: Please enable the `future.v2_normalizeFormMethod` flag to " + + "prepare for the Remix v2 release. Lowercase `useNavigation().formMethod`" + + "values are being normalized to uppercase in v2 to align with the `fetch()` " + + "behavior. For more information, see https://remix.run/docs/hooks/use-navigation" + ); + } + let routes = createClientRoutes( window.__remixManifest.routes, window.__remixRouteModules,