react-call:像 await 函数一样调用 React 组件
react-call 让模态框变成可 await 的函数调用:1KB、零依赖。本文讲解 createCallable 的三步用法与确认对话框完整实战。
react-call 把"打开模态框等用户操作"变成 const ok = await Confirm.call({ title: '删除?' })——组件用 createCallable 包装后即可命令式调用并 await 结果,消灭可见性状态与回调链。
传统方式的痛点
一个确认对话框需要:可见性 state、上下文数据 state、确认/取消两个回调——触发组件在树深处时还要状态提升或 Context 穿透。三行能写完的逻辑膨胀成二十行。
react-call 三步走
npm install react-call
import { createCallable } from 'react-call';
// 1. 包装组件
const Confirm = createCallable<{ title: string }, boolean>(({ call, title }) => (
<dialog open>
<p>{title}</p>
<button onClick={() => call.end(true)}>确认</button>
<button onClick={() => call.end(false)}>取消</button>
</dialog>
));
// 2. 挂载响应器(应用根部)
<Confirm.Root />
// 3. 像函数一样调用
async function handleDelete(item) {
const ok = await Confirm.call({ title: `删除 ${item.name}?` });
if (ok) await deleteItem(item);
}
核心概念
createCallable<Props, Result>:泛型声明入参与返回类型——类型安全贯通;call.end(value):组件内结束调用并回传结果(Promise resolve);.Root:渲染响应器的挂载点,类似 Portal 出口。
完整示例:删除确认
const Confirm = createCallable<{ title: string; danger?: boolean }, boolean>(
({ call, title, danger }) => (
<div className="modal-mask">
<div className="modal">
<h3>{title}</h3>
<button className={danger ? 'btn-danger' : ''} onClick={() => call.end(true)}>
确认
</button>
<button onClick={() => call.end(false)}>取消</button>
</div>
</div>
)
);
function App() {
return (
<>
<ItemList />
<Confirm.Root />
</>
);
}
任何层级的组件都能 await Confirm.call()——不需要把对话框状态提升到公共祖先。
常见问题(FAQ)
Q:与 Context + useReducer 方案比优势?
代码量与心智负担:调用即函数,结果即返回值。传统方案的"状态-回调-清理"模板代码全部消失。
Q:支持 toast、抽屉等其他组件吗?
任何"触发-响应"模式的组件都适合:确认框、toast、表单弹窗、引导提示。串行连续调用(向导流程)也可用 await 链表达。
Q:服务端渲染兼容吗?
兼容(Root 在客户端渲染)。Next.js App Router 项目把 Root 放进客户端组件边界即可。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。