react-flow-code-review — скилл для Claude Code: ревью React Flow
Проверяет код на @xyflow/react по чек-листу: антипаттерны, лишние ре-рендеры, типы. Пригодится, когда граф тормозит или ноды пересоздаются.
npx skills add existential-birds/beagle --skill react-flow-code-review -a claude-codeЧто делает react-flow-code-review
Скилл ведёт ревью по пяти шагам. Сначала ищет в области проверки ReactFlow, ReactFlowProvider, useReactFlow, @xyflow/react, nodeTypes и edgeTypes и возвращает список файлов. Потом для каждого useReactFlow() прослеживает дерево компонентов до ReactFlowProvider и фиксирует несовпадение с номером строки. Дальше смотрит, обёрнуты ли кастомные ноды и рёбра в memo, есть ли типизированные пропсы NodeProps<...>, и стабильны ли ссылки на nodeTypes и edgeTypes.
Находки выдаются с доказательством: путь к файлу и строки либо короткий фрагмент кода. Критичную проблему без ссылки скилл не заявляет. В конце закрываются два чек-листа — производительность и частые ошибки; каждый пункт получает статус «выполнено», «не применимо» с причиной или «открыто» с доказательством.
Внутри разобраны пять антипаттернов: nodeTypes внутри компонента, отсутствие memo, инлайн-колбэки без useCallback, useReactFlow вне провайдера и сложные объекты в данных ноды. Отдельно — мелочи, на которых спотыкаются: контейнер без высоты, забытый импорт @xyflow/react/dist/style.css, кнопка без класса nodrag, строковые литералы вместо Position.Right и прямая мутация массива нод. Скилл рассчитан на проекты с @xyflow/react; если библиотека в коде не используется, искать ему нечего.
Какие задачи решает
- найти причину, по которой все ноды пересоздаются при каждом рендере
- проверить, что кастомные ноды и рёбра обёрнуты в
memo - отловить
useReactFlowвнеReactFlowProviderдо запуска приложения - убрать лишние обновления из-за новых ссылок на объекты в
data - пройтись по чек-листу производительности графа и закрыть открытые пункты
Примеры запросов агенту
- проверь этот компонент с React Flow на антипаттерны и покажи файлы со строками
- почему у меня пересоздаются ноды при каждом рендере? прогони ревью по скиллу
- пройдись по чек-листу производительности в
src/Flow.tsxи отметь, что не выполнено
Как установить react-flow-code-review
1Через Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a claude-code2Вручную: положить файлы
~/.claude/skills/react-flow-code-review/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/react-flow-code-review/
1Через Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a codex2Вручную: положить файлы
.agents/skills/react-flow-code-review/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a cursor2Вручную: положить файлы
.cursor/skills/react-flow-code-review/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install existential-birds/beagle react-flow-code-review2Вручную: положить файлы
.github/skills/react-flow-code-review/SKILL.md1Через Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a gemini-cli2Командой платформы
gemini skills install https://github.com/existential-birds/beagle --path plugins/beagle-react/skills/react-flow-code-reviewВручную: положить файлы
.devin/skills/react-flow-code-review/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-flow-code-review -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о react-flow-code-review
Как установить react-flow-code-review в Claude?
Выполните npx skills add existential-birds/beagle --skill react-flow-code-review -a claude-code. Скилл окажется в ~/.claude/skills/react-flow-code-review/SKILL.md.
Что делает react-flow-code-review?
Проводит ревью кода на @xyflow/react по пяти шагам: ищет файлы с графом, проверяет границу провайдера, стабильность типов и ссылок, затем выдаёт находки со ссылками на строки и закрывает чек-листы производительности и частых ошибок.
Что нужно, чтобы скилл заработал?
Код должен использовать @xyflow/react — скилл ищет ReactFlow, ReactFlowProvider, useReactFlow, nodeTypes и edgeTypes. Для других библиотек графов он не подойдёт.
Исходный текст
SKILL.md · Apache-2.0
---
name: react-flow-code-review
description: Reviews React Flow code for anti-patterns, performance issues, and best practices. Use when reviewing code that uses @xyflow/react, checking for common mistakes, or optimizing node-based UI implementations.
---
# React Flow Code Review
When reviewing React Flow code, complete the gates below in order. Each step has an objective pass condition before moving on.
## Review gates (sequenced)
1. **Locate flow code** — Search the review scope for `ReactFlow`, `ReactFlowProvider`, `useReactFlow`, `@xyflow/react`, `nodeTypes`, and `edgeTypes`. **Pass:** a short list of file paths (or explicit “none in scope” after searching).
2. **Provider boundary** — For each `useReactFlow()` (and other hooks that require the provider), trace the component tree to an enclosing `ReactFlowProvider`, or record a concrete mismatch with **file:line**.
3. **Stable types and memo surfaces** — For each custom node or edge component, note whether it uses `memo` and typed props (`NodeProps<...>`, etc.). For each `nodeTypes` / `edgeTypes` value passed into `<ReactFlow>`, confirm a stable reference (module scope, or `useMemo` with deps you can point to) or flag unstable recreation with **file:line**.
4. **Report with evidence** — For each finding you will deliver, record **file path and line number(s)** (or a minimal quoted snippet). **Pass:** no critical or high-severity issue is stated without that citation.
5. **Close the checklists** — Use [Performance Checklist](#performance-checklist) and [Common Mistakes](#common-mistakes); each item is **satisfied**, **not applicable** (with reason), or **open** with evidence. **Pass:** no item left silently ambiguous.
## Critical Anti-Patterns
### 1. Defining nodeTypes/edgeTypes Inside Components
**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} />;
}
```
### 2. Missing memo() on Custom Nodes/Edges
**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>;
});
```
### 3. Inline Callbacks Without 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} />
```
### 4. Using useReactFlow Outside Provider
```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>
);
}
```
### 5. Storing Complex Objects in 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' } });
```
## 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`
### Edge Rendering
- [ ] Custom edges wrapped in `memo()`
- [ ] edgeTypes defined outside component or memoized
- [ ] Edge path calculations are not duplicated
### State Updates
- [ ] Using functional form of setState: `setNodes((nds) => ...)`
- [ ] Not spreading entire state for single property updates
- [ ] Using `updateNodeData` for data-only changes
- [ ] Batch updates when adding multiple nodes/edges
### Viewport
- [ ] Not calling `fitView()` on every render
- [ ] Using `fitViewOptions` for initial fit only
- [ ] Animation durations are reasonable (< 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>
```
### Missing CSS Import
```tsx
// Required for default styles
import '@xyflow/react/dist/style.css';
```
### Forgetting nodrag on Interactive Elements
```tsx
// BAD - clicking button drags node
<button onClick={handleClick}>Click</button>
// GOOD - prevents drag
<button className="nodrag" onClick={handleClick}>Click</button>
```
### Not Using Position Constants
```tsx
// BAD - string literals
<Handle type="source" position="right" />
// GOOD - type-safe constants
import { Position } from '@xyflow/react';
<Handle type="source" position={Position.Right} />
```
### Mutating Nodes/Edges Directly
```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
));
```
## TypeScript Issues
### 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);
```
### Wrong Props Type
```tsx
// BAD - using wrong type
function CustomNode(props: any) { ... }
// GOOD - correct props type
function CustomNode(props: NodeProps<MyNode>) { ... }
```
## Review Questions
1. Are all custom components memoized?
2. Are nodeTypes/edgeTypes defined outside render?
3. Are callbacks wrapped in useCallback?
4. Is the container sized properly?
5. Are styles imported?
6. Is useReactFlow used inside a provider?
7. Are interactive elements marked with nodrag?
8. Are types used consistently throughout?
© existential-birds · Apache-2.0 · existential-birds/beagle