前言
useFetcher
是 React Router
中一个强大的钩子,用于在不触发页面导航的情况下执行数据加载(GET)或提交(POST)。
一、useFetcher 应用场景:
1、后台数据预加载(如鼠标悬停时加载数据)
2、无刷新表单提交(如点赞、评论)
3、多步骤表单的局部提交
4、与当前页面路由解耦的独立数据操作
二、useFetcher 核心特性
非侵入式操作:不会影响当前路由或 URL。
状态跟踪:提供 state 属性跟踪操作状态(idle/submitting/loading)
。
数据缓存:自动管理相同请求的缓存,避免重复提交。
多实例支持:可在同一页面多次使用,独立管理不同操作。
三、useFetcher 基本使用
3.1、 初始化 Fetcher
import { useFetcher } from "react-router-dom";
function LikeButton() {
const fetcher = useFetcher();
return (
<fetcher.Form method="post" action="/api/like">
<button type="submit">
{fetcher.state === "submitting" ? "点赞中..." : "点赞"}
</button>
</fetcher.Form>
);
}
3.2、useFetcher核心 API 与参数
useFetcher()
返回一个对象,包含以下属性和方法:
属性/方法
Form
: React 组件 用于 替代 <form>
,提交时不会触发页面导航
submit(data, options)
: 函数 用于 手动提交数据(支持 FormData/对象/URL
参数)
load(url)
: 函数 用于 手动触发 GET 请求加载数据
data
: any 表示 最近一次成功操作返回的数据
state
: "idle"/"submitting"/"loading"
表示 当前操作状态
四、useFetcher 完整案例:用户评论功能
4.1、 组件代码
function CommentSection({ postId }) {
const fetcher = useFetcher();
const [commentText, setCommentText] = useState("");
// 显示提交后的评论(包括乐观更新)
const comments = fetcher.data?.comments || [];
return (
<div>
<h3>评论列表</h3>
<ul>
{comments.map((comment) => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
<fetcher.Form
method="post"
action={`/posts/${postId}/comment`}
onSubmit={() => setCommentText("")} // 清空输入框
>
<textarea
name="text"
value={commentText}
onChange={(e) => setCommentText(e.target.value)}
/>
<button type="submit" disabled={fetcher.state !== "idle"}>
{fetcher.state === "submitting" ? "提交中..." : "发布评论"}
</button>
</fetcher.Form>
{/* 显示错误信息 */}
{fetcher.data?.error && (
<div className="error">{fetcher.data.error}</div>
)}
</div>
);
}
4.2、 后端路由处理(示例)
// 路由配置中的 action 函数
export async function commentAction({ request, params }) {
const postId = params.postId;
const formData = await request.formData();
try {
// 模拟 API 调用
const response = await fetch(`/api/posts/${postId}/comment`, {
method: "POST",
body: JSON.stringify({ text: formData.get("text") }),
});
if (!response.ok) throw new Error("评论失败");
const result = await response.json();
// 返回更新后的评论列表
return { comments: result.comments };
} catch (error) {
return { error: error.message };
}
}
五、useFetcher 高级用法:手动控制数据流
5.1、手动提交数据
function SearchBox() {
const fetcher = useFetcher();
const [query, setQuery] = useState("");
// 输入变化时自动搜索(防抖)
useEffect(() => {
const timeoutId = setTimeout(() => {
if (query) {
fetcher.load(`/api/search?q=${query}`);
}
}, 300);
return () => clearTimeout(timeoutId);
}, [query]);
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{/* 显示搜索结果 */}
{fetcher.data?.results?.map((result) => (
<div key={result.id}>{result.name}</div>
))}
</div>
);
}
5.2、处理文件上传
function AvatarUpload() {
const fetcher = useFetcher();
const handleFileChange = (e) => {
const file = e.target.files[0];
const formData = new FormData();
formData.append("avatar", file);
// 手动提交 FormData
fetcher.submit(formData, {
method: "post",
action: "/api/upload-avatar",
encType: "multipart/form-data",
});
};
return (
<div>
<input type="file" onChange={handleFileChange} />
{fetcher.data?.url && (
<img src={fetcher.data.url} alt="用户头像" />
)}
</div>
);
}
六、useFetcher使用注意事项
6.1、路由配置要求
必须使用数据路由(通过 createBrowserRouter
创建路由)
提交的目标路由(action 路径
)需要定义对应的 action 函数
6.2、性能优化
避免高频调用 load/submit
(如搜索框需防抖)
对相同 URL 的请求,React Router
会自动去重
6.3、错误处理
通过 fetcher.data
接收 action
返回的错误信息
使用 try/catch
包裹异步操作并返回错误状态
6.4、与全局状态配合
如果需要更新全局数据(如用户信息),结合 useRevalidator()
重新验证路由加载器
七、useFetcher与普通表单提交的对比
总结:
我们可以用useFetcher
实现高度交互的 Web 应用,同时保持代码的简洁性和可维护性。
它是构建现代 SPA(单页面应用) 中复杂交互(如即时保存、实时搜索)的理想工具。
如有错误之处,欢迎评论指正