React flow - Customizing React Flow: Difference between revisions

From 탱이의 잡동사니
Jump to navigation Jump to search
(Created page with "== Overview == Reactflow 정리 문서 원문은 이곳<ref>https://reactflow.dev/learn/customization/custom-nodes</ref>에서 확인할 수 있다. == Custom Nodes == A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some t...")
 
 
(4 intermediate revisions by the same user not shown)
Line 58: Line 58:
</pre>
</pre>
After putting all together and adding some basic styles we get a custom node that prints text to the console.
After putting all together and adding some basic styles we get a custom node that prints text to the console.
=== Using Multiple Handles ===
As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id "a" and other one "b".
Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.
<pre>
const initialEdges = [
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },
];
</pre>
In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id "a" and the other one from "b". Both edges also have different target nodes:
<pre>
const initialNodes = [
  {
    id: "node-1",
    type: "textUpdater",
    position: { x: 0, y: 0 },
    data: { value: 123 },
  },
  {
    id: "node-2",
    type: "output",
    targetPosition: "top",
    position: { x: 0, y: 200 },
    data: { label: "node 2" },
  },
  {
    id: "node-3",
    type: "output",
    targetPosition: "top",
    position: { x: 200, y: 200 },
    data: { label: "node 3" },
  },
];
</pre>
Note that if you are programmatically changing the position or number of handles in your custom node, you will need to use the useUpdateNodeInternals hook to properly notify ReactFlow of changes. From here you should be abel to build your custom nodes. In most cases we recommend to use custom nodes only. The built-in ones are just basic examples.
=== Custom edges ===
Like custom nodes, parts of a custom edge in React Flow are just React component: that means you can render anything you want along an edge!
=== A basic custom edge ===
An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the <BaseEdge /> component. To calculate the actual SVG path to render, React Flow comes with some handy utility functions:
* getBezierPath
* getSimpleBezierPath
* getSmoothStepPath
* getStraightPath
To kick start out custom edge, we'll just render a straight path between the source and target.
<pre>
import {BaseEdge, getStraightPath} from 'reactflow';
export default function CustomEdge({id, sourceX, sourceY, targetX, targetY}) {
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
  });
  return(
    <>
      <BaseEdge id={id} path={edgePath} />
    </>
  );
}
</pre>
This gives us a straight edge that behaves the same as the default "straight" edge type. To use it, we also need to update the edgeTypes prop on the <ReactFlow /> component.
It's important to define the edgeTypes object outside of the component or to use React's useMemo hook to prevent unnecessary re-renders. React Flow will show a warning in the console if you forget to do this.
<pre>
import ReactFlow from 'reactflow'
import CustomEdge from './CustomEdge'
const edgeTypes = {
  'custom-edge': CustomEdge
}
export function Flow() {
  return <ReactFlow edgeTypes={edgeTypes}... />
}
</pre>
After defining the edgeTypes objects, we can use our new custom edge by setting the type field of an edge to "custom-edge".
=== Adding an edge label ===
One of the more common uses for custom edges is rendering some controls or info along an edge's path. In React Flow we call that an edge label and unlike the edge path, edge labels can be any React component!
To render a custom edge label we must wrap it in the <EdgeLabelRenderer /> component. This is necessary for performance reasons; the edge label renderer is a portal to a single container that all edge labels are rendered into.
Let's add a button to our custom edge that can be used to delete the edge it's attached to:
<pre>
import {
  BaseEdge,
  EdgeLabelRenderer,
  getStraightPath,
  useReactFlow,
} from 'reactflow';
export default function CustomEdge({id, sourceX, sourceY, targetX, target Y}) {
  const {setEdge} = useReactFlow();
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
  });
  return(
    <>
      <BaseEdge id={id} path={edgePath} />
      <EdgeLabelRenderer>
        <button
          onClick={() => setEdges((edges) => edges.filter((e) => e.id !== id))}
        >
          delete
        </button>
      </EdgeLabelRenderer>
    </>
  );
}
</pre>
If we try to use this edge now, we'll see that the button is rendered in the center of the flow (it might be hidden behind "Node A"). Because of the edge label portal, we'll need to do some extra work to position the button ourselves.
Fortunately, the path utils we've already seen can help us with this! Along with the SVG path to render, these functions also return the x and y coordinates of the path's midpoint. We can then use these coordinates to translate our custom edge label's into the right position!
<pre>
export default function CustomEdge({id, sourceX, sourceYm targetX, targetY}) {
  const {setEdges} = useReactFlow();
  const [edgePath, labelX, labelY] = getStraightPath({});
  return(
    ...
    <button
      style={{
        position: 'absolute',
        transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
        pointerEvents: 'all',
      }}
      calssName="nodrag nopan"
      onClick={() => {
        setEdges((es) => es.filter((e) => e.id !== id));
      }}
    >
    ...
  );
}
</pre>
== Theming ==
React Flow has been built with deep customization in mind. Many of our users fully transform the look and feel of React Flow to match their own brand or design system. This guide will introduce you to the different ways you can customize React Flow's appearance.
=== Default styles ===
React Flow's default styles are enough to get going with the built-in nodes. They provide some sensible defaults for styles like padding, border radius, and animated edges.
You'll typically load these default styles by importing them in you App.jsx file or other entry point
<pre>
import 'reactflow/dist/style.css';
</pre>
Without dipping into custom nodes and edges, there are three ways you can style React Flow's basic look:
* Passing inline styles through style props.
* Overriding the built-in classes with custom CSS.
* Overriding the CSS variables React Flow uses.
=== Customizing with style props ===
The easiest way to start customizing the look and feel of your flows is to use the style prop found on many of React Flow's components to inline your own CSS.
<pre>
import ReactFlow from 'reactflow'
const styles = {
  background: 'red',
  width: '100%',
  height: 300,
};
export default function Flow() {
  return <ReactFlow style={styles} nodes={[...]} edges={[...]} />
}
</pre>




[[category:react]]
[[category:react]]

Latest revision as of 07:35, 6 March 2024

Overview

Reactflow 정리 문서 원문은 이곳<ref>https://reactflow.dev/learn/customization/custom-nodes</ref>에서 확인할 수 있다.

Custom Nodes

A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.

Implementing the Custom Node

A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:

import {useCallback} from 'react';
import {Handle, Position} from 'reactflow';

const handleStype = {left: 10 };

function TextUpdaterNode({data}) {
  const onChange = useCallback((evt) => {
    console.log(evt.target.value);
  }, []);

  return(
  <>
    <Handle type="target" position={Position.Top} />
    <div>
      <label htmlFor="text">Text:</label>
      <input id="text" name="text" onChange={onChange} className="nodrag" />
    </div>
    <Handle type="source" position={Position.Botton} id="a" />
    <Handle
      type="source"
      position={Position.Botton}
      id="b"
      stype={handleStyle}
    </Handle>
  </>
  );
}

As you see we've added the class name "nodrag" to the input. This prevents dragging within the input field and lets us select text for example.

Adding the Node Type

You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.

const nodeTypes = useMemo(() => ({ textUpdater: TextUpdaterNode }), []);

return <ReactFlow nodeTypes={nodeTypes} />;

After defining your new node type, you can use it by using the type node option:

const nodes = [
  {
    id: 'node-1',
    type: 'textUpdater',
    position: {x: 0, y: 0},
    data: {value: 123},
  },
];

After putting all together and adding some basic styles we get a custom node that prints text to the console.

Using Multiple Handles

As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id "a" and other one "b". Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.

const initialEdges = [
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },
];

In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id "a" and the other one from "b". Both edges also have different target nodes:

const initialNodes = [
  {
    id: "node-1",
    type: "textUpdater",
    position: { x: 0, y: 0 },
    data: { value: 123 },
  },
  {
    id: "node-2",
    type: "output",
    targetPosition: "top",
    position: { x: 0, y: 200 },
    data: { label: "node 2" },
  },
  {
    id: "node-3",
    type: "output",
    targetPosition: "top",
    position: { x: 200, y: 200 },
    data: { label: "node 3" },
  },
];

Note that if you are programmatically changing the position or number of handles in your custom node, you will need to use the useUpdateNodeInternals hook to properly notify ReactFlow of changes. From here you should be abel to build your custom nodes. In most cases we recommend to use custom nodes only. The built-in ones are just basic examples.

Custom edges

Like custom nodes, parts of a custom edge in React Flow are just React component: that means you can render anything you want along an edge!

A basic custom edge

An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the <BaseEdge /> component. To calculate the actual SVG path to render, React Flow comes with some handy utility functions:

  • getBezierPath
  • getSimpleBezierPath
  • getSmoothStepPath
  • getStraightPath

To kick start out custom edge, we'll just render a straight path between the source and target.

import {BaseEdge, getStraightPath} from 'reactflow';

export default function CustomEdge({id, sourceX, sourceY, targetX, targetY}) {
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
  });

  return(
    <>
      <BaseEdge id={id} path={edgePath} />
    </>
  );
}

This gives us a straight edge that behaves the same as the default "straight" edge type. To use it, we also need to update the edgeTypes prop on the <ReactFlow /> component.

It's important to define the edgeTypes object outside of the component or to use React's useMemo hook to prevent unnecessary re-renders. React Flow will show a warning in the console if you forget to do this.

import ReactFlow from 'reactflow'
import CustomEdge from './CustomEdge'

const edgeTypes = {
  'custom-edge': CustomEdge
}

export function Flow() {
  return <ReactFlow edgeTypes={edgeTypes}... />
}

After defining the edgeTypes objects, we can use our new custom edge by setting the type field of an edge to "custom-edge".

Adding an edge label

One of the more common uses for custom edges is rendering some controls or info along an edge's path. In React Flow we call that an edge label and unlike the edge path, edge labels can be any React component!

To render a custom edge label we must wrap it in the <EdgeLabelRenderer /> component. This is necessary for performance reasons; the edge label renderer is a portal to a single container that all edge labels are rendered into.

Let's add a button to our custom edge that can be used to delete the edge it's attached to:

import {
  BaseEdge,
  EdgeLabelRenderer,
  getStraightPath,
  useReactFlow,
} from 'reactflow';

export default function CustomEdge({id, sourceX, sourceY, targetX, target Y}) {
  const {setEdge} = useReactFlow();
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
  });

  return(
    <>
      <BaseEdge id={id} path={edgePath} />
      <EdgeLabelRenderer>
        <button
          onClick={() => setEdges((edges) => edges.filter((e) => e.id !== id))}
        >
          delete
        </button>
      </EdgeLabelRenderer>
    </>
  );
}

If we try to use this edge now, we'll see that the button is rendered in the center of the flow (it might be hidden behind "Node A"). Because of the edge label portal, we'll need to do some extra work to position the button ourselves.

Fortunately, the path utils we've already seen can help us with this! Along with the SVG path to render, these functions also return the x and y coordinates of the path's midpoint. We can then use these coordinates to translate our custom edge label's into the right position!

export default function CustomEdge({id, sourceX, sourceYm targetX, targetY}) {
  const {setEdges} = useReactFlow();
  const [edgePath, labelX, labelY] = getStraightPath({});

  return(
    ...
    <button
      style={{
        position: 'absolute',
        transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
        pointerEvents: 'all',
      }}
      calssName="nodrag nopan"
      onClick={() => {
        setEdges((es) => es.filter((e) => e.id !== id));
      }}
    >
    ...
  );
}

Theming

React Flow has been built with deep customization in mind. Many of our users fully transform the look and feel of React Flow to match their own brand or design system. This guide will introduce you to the different ways you can customize React Flow's appearance.

Default styles

React Flow's default styles are enough to get going with the built-in nodes. They provide some sensible defaults for styles like padding, border radius, and animated edges.

You'll typically load these default styles by importing them in you App.jsx file or other entry point

import 'reactflow/dist/style.css';

Without dipping into custom nodes and edges, there are three ways you can style React Flow's basic look:

  • Passing inline styles through style props.
  • Overriding the built-in classes with custom CSS.
  • Overriding the CSS variables React Flow uses.

Customizing with style props

The easiest way to start customizing the look and feel of your flows is to use the style prop found on many of React Flow's components to inline your own CSS.

import ReactFlow from 'reactflow'

const styles = {
  background: 'red',
  width: '100%',
  height: 300,
};

export default function Flow() {
  return <ReactFlow style={styles} nodes={[...]} edges={[...]} />
}