Smart Flow 013/014:补充学习记录
Smart Flow 013/014:补充学习记录
添加workflow表单,包括删除更新等功能
空白页,加载页面,错误页面三大组件
空白页的props,需要一个新建的函数,意味着当前空白的时候可以新建一个工作流
codex回答:我看了这两个提交:
013: editor header ui014: 建议editor页面,实现基本的workflow页面
对前端开发来说,最值得学的是这些点。
1. Suspense + ErrorBoundary + Hydration 的页面组织
在 013 里,editor 页面从简单的:
return <p>Workflow id: {workflowId}</p>;变成:
<HydrateClient>
<EditorHeader workflowId={workflowId} />
<ErrorBoundary fallback={<EditorError />}>
<Suspense fallback={<EditorLoading />}>
<Editor workflowId={workflowId} />
</Suspense>
</ErrorBoundary>
</HydrateClient>这个值得重点学。
原因:这是 Next.js App Router 里常见的结构。服务端页面负责鉴权、拿 params、包 hydration;客户端组件负责交互;加载和错误状态用 Suspense / ErrorBoundary 接住。
学习重点:
- Server Component 和 Client Component 的边界
HydrateClient的作用- 为什么数据请求组件要放进
Suspense - 为什么错误 UI 不应该直接写在业务组件里
2. inline edit 的完整交互流程
013 里的 EditorNameInput 很值得学:
- 点击文字进入编辑状态
- input 自动 focus/select
Enter保存Escape取消blur保存- 保存失败恢复旧值
- mutation 成功后 invalidate query
这是一个很典型的前端交互组件。
难点不在 UI,而在状态同步:
const [name, setName] = useState(workflow.name);
useEffect(() => {
if (workflow.name) {
setName(workflow.name);
}
}, [workflow.name]);你要理解:远端数据 workflow.name 和本地编辑态 name 是两份状态,什么时候同步、什么时候不应该同步,是前端开发里的核心问题。
3. React Query / tRPC 的缓存失效
013 里改名成功后做了:
queryClient.invalidateQueries(trpc.workflows.getMany.queryOptions({}));
queryClient.invalidateQueries(
trpc.workflows.getOne.queryOptions({ id: data.id }),
);这个很重要。
因为改名会影响两个地方:
- 列表页里的 workflow name
- editor header 里的 workflow name
所以 mutation 成功后,要同时让列表缓存和详情缓存失效。
前端要学的不是“会调接口”,而是:一次 mutation 会影响哪些 query cache。
4. 图编辑器的数据建模
014 最值得学的是 Prisma schema 这部分:
model Workflow {
nodes Node[]
connections Connection[]
}
model Node {
workflowId String
type NodeType
position Json
data Json
}
model Connection {
fromNodeId String
toNodeId String
fromOutput String
toInput String
}这是从普通 CRUD 进入“可视化编辑器”的关键。
难点是:前端画布不是单纯页面状态,它背后必须有稳定的数据模型。
你要理解:
- Workflow 是一个图
- Node 是图里的点
- Connection 是图里的边
- React Flow 只是渲染层
- 数据库模型才是最终状态来源
5. 后端数据转换成 React Flow 数据结构
014 里 getOne 不再直接返回 Prisma workflow,而是转换成 React Flow 需要的格式:
const nodes: Node[] = workflow.nodes.map((node) => ({
id: node.id,
type: node.type,
position: node.position as { x: number; y: number },
data: node.data as Record<string, unknown>,
}));
const edges: Edge[] = workflow.connections.map((connection) => ({
id: connection.id,
source: connection.fromNodeId,
target: connection.toNodeId,
sourceHandle: connection.fromOutput,
targetHandle: connection.toInput,
}));这个非常值得学。
原因:真实项目里,后端数据结构通常不等于前端组件库需要的数据结构。你需要做 adapter/mapper。
这里的学习点是:
- 数据库字段和 UI library 字段如何对应
- 不要让 React Flow 直接依赖 Prisma 原始结构
- API 返回值应该面向前端使用场景设计
6. React Flow 的受控状态模式
014 里的 editor 开始使用 React Flow:
const [nodes, setNodes] = useState<Node[]>(workflow.nodes);
const [edges, setEdges] = useState<Edge[]>(workflow.edges);
const onNodesChange = useCallback(
(changes) => setNodes((snapshot) => applyNodeChanges(changes, snapshot)),
[],
);
const onEdgesChange = useCallback(
(changes) => setEdges((snapshot) => applyEdgeChanges(changes, snapshot)),
[],
);
const onConnect = useCallback(
(params) => setEdges((snapshot) => addEdge(params, snapshot)),
[],
);这是 React Flow 的核心模式。
要学的是:
nodes/edges是受控状态- 拖动节点不会自动改你的状态,必须处理
onNodesChange - 连线不会自动保存,必须处理
onConnect - 当前这里只是前端临时状态,还没有持久化到数据库
这个是做 workflow editor、低代码编辑器、流程图工具的基础。
7. 自定义节点注册机制
014 里加了:
export const nodeComponents = {
[NodeType.INITIAL]: InitialNode,
} as const satisfies NodeTypes;然后传给:
<ReactFlow nodeTypes={nodeComponents} />这个也值得学。
它解决的是:数据库里存的是 NodeType.INITIAL,前端要根据 type 渲染对应 React 组件。
以后你加:
- Slack node
- Discord node
- OpenAI node
- Condition node
- Webhook node
都会沿用这个模式。
8. 全局 CSS 引入位置
014 里加了:
import "@xyflow/react/dist/style.css";这也是实际开发里常见坑。
很多组件库不是只装包就能工作,还需要全局样式。React Flow 没有这个 CSS 会出现节点、连线、控制器样式异常。
我建议你重点学习顺序
- 先学
013的页面结构:HydrateClient+Suspense+ErrorBoundary - 再学
013的 inline edit:本地状态、远端状态、mutation、缓存失效 - 然后学
014的图数据模型:Workflow / Node / Connection - 再学
014的 mapper:Prisma 数据转 React Flownodes/edges - 最后学 React Flow 受控状态:
onNodesChange、onEdgesChange、onConnect
一句话总结:
013 值得学的是“一个现代 Next.js 数据页面怎么组织”;014 值得学的是“如何把普通 CRUD 升级成可视化 workflow editor”。
增加节点选择器,那么肯定要抽象一个选择器,同时还要注册不同类型的节点
