如何修复Remix错误:必须在数据路由器内使用useFetcher?

huangapple go评论76阅读模式
英文:

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

问题

这是您提供的文本的中文翻译:

"我是 Remix 的新手(也是后端编程的新手),在解决问题时感到很迷茫。我正在尝试使用 UseFetcher 允许在“类似待办事项”的应用程序中进行非导航数据的变化。Remix 文档并没有明确说明我需要在数据路由中使用它,示例也没有帮助我搞清楚。

以下是我在 root.tsx 中的 App 组件的样子:

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>
  );
}

以及用于显示项目列表的 routes/goods.tsx(其中大部分是从默认的 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>
  );
}

这导致了错误:Error: useFetcher must be used within a data router.

然后我尝试遵循将 App 封装在数据路由内的说明,使用 createBrowserRouter 编写了我的 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>
  );
}

我不知道为每个元素添加什么加载程序。我尝试将异步加载程序分配给一个常量,并将其添加到路由的构造函数中,但是收到错误:The expected type comes from property 'loader' which is declared here on type 'RouteObject',所以我将它留空了。

这段代码导致了 ReferenceError: document is not defined 的错误,这显然是因为我没有正确使用此路由的语法或结构。有人可以提供关于我在这种情况下应该如何使用 createBrowserRouter 的指导吗?我知道我需要以某种方式使用 RouterProvider 组件,但我没有足够的经验来看出前进的路径。我在这里漏掉了什么?"

请注意,这是您提供的文本的中文翻译,如果您需要进一步的帮助或有任何问题,请随时提出。

英文:

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 (
&lt;html lang=&quot;en&quot; className=&quot;h-full&quot;&gt;
&lt;head&gt;
&lt;Meta /&gt;
&lt;Links /&gt;
&lt;/head&gt;
&lt;body className=&quot;h-full&quot;&gt;
&lt;Outlet /&gt;
&lt;ScrollRestoration /&gt;
&lt;Scripts /&gt;
&lt;LiveReload /&gt;
&lt;/body&gt;
&lt;/html&gt;
);
}

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(&quot;title&quot;);
const id = formData.get(&quot;id&quot;);
if (typeof title !== &quot;string&quot; || title.length === 0) {
return json(
{ errors: { title: &quot;Title is required&quot; } },
{ status: 400 }
);
}
const good = await updateGood({ title, id });
return null;
}
export default function GoodsPage() {
const data = useLoaderData&lt;typeof loader&gt;();
const user = useUser();
return (
&lt;div className=&quot;flex h-full min-h-screen flex-col&quot;&gt;
&lt;Outlet /&gt;
&lt;main className=&quot;flex h-full bg-white&quot;&gt;
&lt;div className=&quot;h-full w-80 border-r bg-gray-50&quot;&gt;
{data.completedGoodListItems.length === 0 ? (
&lt;p className=&quot;p-4&quot;&gt;No goods yet&lt;/p&gt;
) : (
&lt;&gt;
&lt;h2&gt;Incomplete&lt;/h2&gt;
&lt;ol&gt;
{data.completedGoodListItems.map((good) =&gt; (
&lt;GoodItem key={good.id} good={good}&gt;&lt;/GoodItem&gt;
))}
&lt;/ol&gt;
&lt;/&gt;
)}
&lt;&gt;
&lt;h2&gt;Completed&lt;/h2&gt;
&lt;ol&gt;
{data.incompleteGoodListItems.map((good) =&gt; (
&lt;GoodItem key={good.id} good={good}&gt;&lt;/GoodItem&gt;
))}
&lt;/ol&gt;
&lt;/&gt;
&lt;/div&gt;
&lt;Form action=&quot;/logout&quot; method=&quot;post&quot;&gt;
&lt;button
type=&quot;submit&quot;
className=&quot;rounded bg-slate-600 py-2 px-4 text-blue-100 hover:bg-blue-500 active:bg-blue-600&quot;
&gt;
Logout
&lt;/button&gt;
&lt;/Form&gt;
&lt;/main&gt;
&lt;/div&gt;
);
}
function GoodItem ({ good }) { 
const fetcher = useFetcher();
return (
&lt;li&gt;
&lt;fetcher.Form method=&quot;post&quot;&gt;
&lt;input type=&quot;text&quot; defaultValue={good.title}&gt;&lt;/input&gt;
&lt;/fetcher.Form&gt;
&lt;/li&gt;
)}

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: &quot;/&quot;,
element: &lt;App /&gt;,
// loader: rootLoader,
children: [
{
path: &quot;/goods&quot;,
element: &lt;GoodsPage /&gt;,
// loader: loaderName,
},
],
},
]);
// @ts-ignore
ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(
&lt;RouterProvider router={router} /&gt;
);
export default function App() {
return (
&lt;html lang=&quot;en&quot; className=&quot;h-full&quot;&gt;
&lt;head&gt;
&lt;Meta /&gt;
&lt;Links /&gt;
&lt;/head&gt;
&lt;body className=&quot;h-full&quot;&gt;
&lt;Outlet /&gt;
&lt;ScrollRestoration /&gt;
&lt;Scripts /&gt;
&lt;LiveReload /&gt;
&lt;/body&gt;
&lt;/html&gt;
);
}

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?

答案1

得分: 1

你很可能从错误的包中导入了 useFetcher。请确保你是从 @remix-run/react 导入它:

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

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

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

huangapple
  • 本文由 发表于 2023年1月6日 13:36:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75027298.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定