react-flow-code-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Flow Code Review
React Flow代码审查
Critical Anti-Patterns
关键反模式
1. Defining nodeTypes/edgeTypes Inside Components
1. 在组件内部定义nodeTypes/edgeTypes
Problem: Causes all nodes to re-mount on every render.
tsx
// BAD - recreates object every render
function Flow() {
const nodeTypes = { custom: CustomNode }; // WRONG
return <ReactFlow nodeTypes={nodeTypes} />;
}
// GOOD - defined outside component
const nodeTypes = { custom: CustomNode };
function Flow() {
return <ReactFlow nodeTypes={nodeTypes} />;
}
// GOOD - useMemo if dynamic
function Flow() {
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
return <ReactFlow nodeTypes={nodeTypes} />;
}问题:导致所有节点在每次渲染时重新挂载。
tsx
// 错误示例 - 每次渲染都会重新创建对象
function Flow() {
const nodeTypes = { custom: CustomNode }; // 错误写法
return <ReactFlow nodeTypes={nodeTypes} />;
}
// 正确示例 - 在组件外部定义
const nodeTypes = { custom: CustomNode };
function Flow() {
return <ReactFlow nodeTypes={nodeTypes} />;
}
// 正确示例 - 动态场景下使用useMemo
function Flow() {
const nodeTypes = useMemo(() => ({ custom: CustomNode }), []);
return <ReactFlow nodeTypes={nodeTypes} />;
}2. Missing memo() on Custom Nodes/Edges
2. 自定义节点/Edges未使用memo()包裹
Problem: Custom components re-render on every parent update.
tsx
// BAD - no memoization
function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
}
// GOOD - wrapped in memo
import { memo } from 'react';
const CustomNode = memo(function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
});问题:自定义组件会在父组件每次更新时重新渲染。
tsx
// 错误示例 - 未使用记忆化
function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
}
// 正确示例 - 使用memo包裹
import { memo } from 'react';
const CustomNode = memo(function CustomNode({ data }: NodeProps) {
return <div>{data.label}</div>;
});3. Inline Callbacks Without useCallback
3. 内联回调未使用useCallback
Problem: Creates new function references, breaking memoization.
tsx
// BAD - inline callback
<ReactFlow
onNodesChange={(changes) => setNodes(applyNodeChanges(changes, nodes))}
/>
// GOOD - memoized callback
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
<ReactFlow onNodesChange={onNodesChange} />问题:创建新的函数引用,破坏记忆化效果。
tsx
// 错误示例 - 内联回调
<ReactFlow
onNodesChange={(changes) => setNodes(applyNodeChanges(changes, nodes))}
/>
// 正确示例 - 记忆化回调
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
);
<ReactFlow onNodesChange={onNodesChange} />4. Using useReactFlow Outside Provider
4. 在Provider外部使用useReactFlow
tsx
// BAD - will throw error
function App() {
const { getNodes } = useReactFlow(); // ERROR: No provider
return <ReactFlow ... />;
}
// GOOD - wrap in provider
function FlowContent() {
const { getNodes } = useReactFlow(); // Works
return <ReactFlow ... />;
}
function App() {
return (
<ReactFlowProvider>
<FlowContent />
</ReactFlowProvider>
);
}tsx
// 错误示例 - 会抛出错误
function App() {
const { getNodes } = useReactFlow(); // 错误:无Provider包裹
return <ReactFlow ... />;
}
// 正确示例 - 用Provider包裹
function FlowContent() {
const { getNodes } = useReactFlow(); // 正常工作
return <ReactFlow ... />;
}
function App() {
return (
<ReactFlowProvider>
<FlowContent />
</ReactFlowProvider>
);
}5. Storing Complex Objects in Node Data
5. 在Node Data中存储复杂对象
Problem: Reference equality checks fail, causing unnecessary updates.
tsx
// BAD - new object reference every time
setNodes(nodes.map(n => ({
...n,
data: { ...n.data, config: { nested: 'value' } } // New object each time
})));
// GOOD - use updateNodeData for targeted updates
const { updateNodeData } = useReactFlow();
updateNodeData(nodeId, { config: { nested: 'value' } });问题:引用相等性检查失败,导致不必要的更新。
tsx
// 错误示例 - 每次都创建新对象引用
setNodes(nodes.map(n => ({
...n,
data: { ...n.data, config: { nested: 'value' } } // 每次生成新对象
})));
// 正确示例 - 使用updateNodeData进行针对性更新
const { updateNodeData } = useReactFlow();
updateNodeData(nodeId, { config: { nested: 'value' } });Performance Checklist
性能检查清单
Node Rendering
节点渲染
- Custom nodes wrapped in
memo() - nodeTypes defined outside component or memoized
- Heavy computations inside nodes use
useMemo - Event handlers use
useCallback
- 自定义节点已用包裹
memo() - nodeTypes定义在组件外部或已记忆化
- 节点内的重计算逻辑使用
useMemo - 事件处理函数使用
useCallback
Edge Rendering
连线渲染
- Custom edges wrapped in
memo() - edgeTypes defined outside component or memoized
- Edge path calculations are not duplicated
- 自定义连线已用包裹
memo() - edgeTypes定义在组件外部或已记忆化
- 连线路径计算未重复执行
State Updates
状态更新
- Using functional form of setState:
setNodes((nds) => ...) - Not spreading entire state for single property updates
- Using for data-only changes
updateNodeData - Batch updates when adding multiple nodes/edges
- 使用setState的函数式写法:
setNodes((nds) => ...) - 不会为单个属性更新而展开整个状态
- 仅更新数据时使用
updateNodeData - 添加多个节点/连线时批量更新
Viewport
视口
- Not calling on every render
fitView() - Using for initial fit only
fitViewOptions - Animation durations are reasonable (< 500ms)
- 不会在每次渲染时调用
fitView() - 仅在初始适配时使用
fitViewOptions - 动画时长合理(< 500ms)
Common Mistakes
常见错误
Missing Container Height
缺少容器高度
tsx
// BAD - no height, flow won't render
<ReactFlow nodes={nodes} edges={edges} />
// GOOD - explicit dimensions
<div style={{ width: '100%', height: '100vh' }}>
<ReactFlow nodes={nodes} edges={edges} />
</div>tsx
// 错误示例 - 无高度,Flow无法渲染
<ReactFlow nodes={nodes} edges={edges} />
// 正确示例 - 显式设置尺寸
<div style={{ width: '100%', height: '100vh' }}>
<ReactFlow nodes={nodes} edges={edges} />
</div>Missing CSS Import
缺少CSS导入
tsx
// Required for default styles
import '@xyflow/react/dist/style.css';tsx
// 需要导入默认样式
import '@xyflow/react/dist/style.css';Forgetting nodrag on Interactive Elements
未在交互元素上添加nodrag
tsx
// BAD - clicking button drags node
<button onClick={handleClick}>Click</button>
// GOOD - prevents drag
<button className="nodrag" onClick={handleClick}>Click</button>tsx
// 错误示例 - 点击按钮会拖动节点
<button onClick={handleClick}>点击</button>
// 正确示例 - 阻止拖动
<button className="nodrag" onClick={handleClick}>点击</button>Not Using Position Constants
未使用Position常量
tsx
// BAD - string literals
<Handle type="source" position="right" />
// GOOD - type-safe constants
import { Position } from '@xyflow/react';
<Handle type="source" position={Position.Right} />tsx
// 错误示例 - 使用字符串字面量
<Handle type="source" position="right" />
// 正确示例 - 类型安全的常量
import { Position } from '@xyflow/react';
<Handle type="source" position={Position.Right} />Mutating Nodes/Edges Directly
直接修改Nodes/Edges
tsx
// BAD - direct mutation
nodes[0].position = { x: 100, y: 100 };
setNodes(nodes);
// GOOD - immutable update
setNodes(nodes.map(n =>
n.id === '1' ? { ...n, position: { x: 100, y: 100 } } : n
));tsx
// 错误示例 - 直接修改
nodes[0].position = { x: 100, y: 100 };
setNodes(nodes);
// 正确示例 - 不可变更新
setNodes(nodes.map(n =>
n.id === '1' ? { ...n, position: { x: 100, y: 100 } } : n
));TypeScript Issues
TypeScript问题
Missing Generic Types
缺少泛型类型
tsx
// BAD - loses type safety
const [nodes, setNodes] = useNodesState(initialNodes);
// GOOD - explicit types
type MyNode = Node<{ value: number }, 'custom'>;
const [nodes, setNodes] = useNodesState<MyNode>(initialNodes);tsx
// 错误示例 - 失去类型安全
const [nodes, setNodes] = useNodesState(initialNodes);
// 正确示例 - 显式声明类型
type MyNode = Node<{ value: number }, 'custom'>;
const [nodes, setNodes] = useNodesState<MyNode>(initialNodes);Wrong Props Type
错误的Props类型
tsx
// BAD - using wrong type
function CustomNode(props: any) { ... }
// GOOD - correct props type
function CustomNode(props: NodeProps<MyNode>) { ... }tsx
// 错误示例 - 使用错误类型
function CustomNode(props: any) { ... }
// 正确示例 - 使用正确的Props类型
function CustomNode(props: NodeProps<MyNode>) { ... }Review Questions
审查问题清单
- Are all custom components memoized?
- Are nodeTypes/edgeTypes defined outside render?
- Are callbacks wrapped in useCallback?
- Is the container sized properly?
- Are styles imported?
- Is useReactFlow used inside a provider?
- Are interactive elements marked with nodrag?
- Are types used consistently throughout?
- 所有自定义组件都已记忆化处理?
- nodeTypes/edgeTypes是否定义在渲染外部?
- 回调函数是否已用useCallback包裹?
- 容器尺寸是否设置正确?
- 是否已导入样式文件?
- useReactFlow是否在Provider内部使用?
- 交互元素是否已标记nodrag?
- 类型是否在整个项目中保持一致?