英文:
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 (
<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?
答案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 "@remix-run/react";
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论