简体   繁体   中英

How do I fix the Remix Error: useFetcher must be used within a data router?

I'm new to Remix (and backend programming in general) and feeling lost troubleshooting this. I'm trying to UseFetcher to allow for non-navigational data mutations in a "todo-like" application. Remix docs doesn't explicitly say I need to be using it within a data router, and the examples don't clear up my confusion at all.

Here's what my App component looks like in root.tsx:


export default function App() {
  return (
    <html lang="en" className="h-full">
      <head>
        <Meta />
        <Links />
      </head>
      <body className="h-full">
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

And my routes/goods.tsx for displaying a list of items (much of this is adapted from the default Indie Stack):


export async function action({ request }: ActionArgs) {
  const formData = await request.formData();
  const title = formData.get("title");
  const id = formData.get("id");

  if (typeof title !== "string" || title.length === 0) {
    return json(
      { errors: { title: "Title is required" } },
      { status: 400 }
    );
  }

  const good = await updateGood({ title, id });

  return null;
}

export default function GoodsPage() {
  const data = useLoaderData<typeof loader>();
  const user = useUser();

  return (
    <div className="flex h-full min-h-screen flex-col">
        <Outlet />
        <main className="flex h-full bg-white">
        <div className="h-full w-80 border-r bg-gray-50">
            {data.completedGoodListItems.length === 0 ? (
            <p className="p-4">No goods yet</p>
            ) : (
              <>
                <h2>Incomplete</h2>
                <ol>
                  {data.completedGoodListItems.map((good) => (
                     <GoodItem key={good.id} good={good}></GoodItem>
                  ))}
                 </ol>
              </>
              )}
              <>
              <h2>Completed</h2>
              <ol>
                {data.incompleteGoodListItems.map((good) => (
                  <GoodItem key={good.id} good={good}></GoodItem>
                ))}
              </ol>
            </>
          </div>
          <Form action="/logout" method="post">
            <button
            type="submit"
            className="rounded bg-slate-600 py-2 px-4 text-blue-100 hover:bg-blue-500 active:bg-blue-600"
            >
            Logout
            </button>
          </Form>
        </main>

    </div>
  );
}

function GoodItem ({ good }) { 
  const fetcher = useFetcher();

  return (
    <li>
      <fetcher.Form method="post">
        <input type="text" defaultValue={good.title}></input>
      </fetcher.Form>
    </li>
)}

This results in Error: useFetcher must be used within a data router.

So then I try to follow the instructions for encapsulating the App within a data router using createBrowserRouter which leads me to writing this code in my root.tsx:

async function loader({ request }: LoaderArgs) {
  return json({
    user: await getUser(request),
  });
}

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
    // loader: rootLoader,
    children: [
      {
        path: "/goods",
        element: <GoodsPage />,
        // loader: loaderName,
      },
    ],
  },
]);

// @ts-ignore
ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

export default function App() {
  return (
    <html lang="en" className="h-full">
      <head>
        <Meta />
        <Links />
      </head>
      <body className="h-full">
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

I didn't know what to add for the loaders for each element. I tried assigning the async loader to a const and adding it into the constructor for router, but I received the error: The expected type comes from property 'loader' which is declared here on type 'RouteObject' so I just left it blank.

This code results in ReferenceError: document is not defined certainly because I don't have the syntax or structure correct for this router. Can someone provide some guidance on how I should be using createBrowserRouter in this context? I know I need to use the RouterProvider component in some way, but I don't have enough experience to see the path forward. What am I missing here?

You are most probably importing useFetcher from an incorrect package. Make sure that you are importing it from @remix-run/react :

import { useFetcher } from "@remix-run/react";

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM