首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用REACTFLOW的自定义节点;创建节点后将附加数据保存到节点

使用REACTFLOW的自定义节点;创建节点后将附加数据保存到节点
EN

Stack Overflow用户
提问于 2022-08-27 03:44:32
回答 3查看 579关注 0票数 5

这是我对react-flow的第一次介绍。我希望创建一个自定义节点,在创建之后,用户可以向该节点输入信息并保存/显示它。在react-flow 文献资料 on 自定义节点中,它们有一个类似的示例,其中创建了一个用于console.logs用户输入的TextUpdaterNode

我不是通过控制台记录它,而是寻找一种将信息保存到节点本身并在节点上显示它的方法。例如,如果用户要输入24,将男性输入到输入并按下enter,我希望使用下面的信息更新节点。

我有什么办法可以做到这一点?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-09-13 11:33:55

我设法想出了一个解决方案来创建这样一个自定义节点,允许您输入、保存和显示信息。我已经尝试包括相关的信息和代码块,我在下面使用。

自定义节点

代码语言:javascript
运行
复制
import { useCallback } from 'react';
import { Handle, Position} from 'react-flow-renderer';
const handleStyle = { left: 10 };
//Custom node requires props of data to be passed to it.
function CustomNode({ data }) {    
    let serviceType = "offered";
    //This handles pressing enter inside the description
    const handleKeyDown = (evt) => {
        if (evt.key === "Enter") {
            //Check if empty string
            if (evt.target.value.length !== 0) {
                //This code is because services are either offered or borrowed.
              if (serviceType === "offered") {
                data.serviceOffered.push(evt.target.value);
              } else if (serviceType === "borrowed") {
                data.serviceBorrowed.push(evt.target.value);
              }
                //Clearing input after pressing enter
              evt.currentTarget.value = "";
            }
        }

  };
  const onChange = useCallback((evt) => {
    //Update service type without pressing enter
    serviceType = evt.target.value;
  });

  return (
    <div className="text-updater-node">
      <Handle type="target" position={Position.Top} />
      <div>
        <p>Entity</p>
        <label htmlFor="text"><p className='nodeTitle'>{data.label}</p></label>
        <input id="text" name="text" onKeyDown={handleKeyDown} /> 
        <select name="type" onChange={onChange}>
          <option value="offered" >Offered </option>
          <option value="borrowed">Borrowed</option>
        </select>
        <div className="info">
            {/* This is where the description information is displayed. It checks if it is empty, if not it loops through and displays it.  */}
            <h2>Service Borrowed</h2>
            <ul>
              {data.serviceBorrowed.length? data.serviceBorrowed.map(service => (<li key={service}>{service}</li>)) : <span></span>} 
            </ul>
            <h2>Service Offered</h2> 
            <ul>
              {data.serviceOffered.length? data.serviceOffered.map(service => (<li key={service}>{service}</li>)) : <span></span>}
            </ul>
        </div>
      </div>
      <Handle type="source" position={Position.Bottom} id="a" style={handleStyle} />
      <Handle type="source" position={Position.Bottom} id="b" />
    </div>
  );
}
export default CustomNode;

我有一个具有以下代码块的父reactFlow组件。重要的是设置react flow的自定义节点类型,并传入包含要呈现的节点和边缘的信息的object

代码语言:javascript
运行
复制
import { Fragment, useCallback, useState } from "react";
import ReactFlow, {
  addEdge,
  applyEdgeChanges,
  applyNodeChanges,
} from "react-flow-renderer";
import initialNodes from "../data/nodes"; //This both ended up being empty file
import initialEdges from "../data/edges"; //This both ended up being empty file
import CustomNode from "./customNode";
import "./customNode.css";
//Set nodetype as Custom node, IMPORTANT!
const nodeTypes = { customNode: CustomNode };

function Flow() {
  const defaultEdgeOptions = { animated: true };
  //Input Elements
  const [name, setName] = useState("");
  const addNode = () => {
    setNodes((e) =>
      e.concat({
        id: (e.length + 1).toString(),
        data: { label: `${name}`, serviceOffered: [], serviceBorrowed: [] },
        position: { x: 0, y: 0 },
        type: "customNode",
      })
    );
  };
  //Nodes and edges containing information of the nodes and edges
  const [nodes, setNodes] = useState(initialNodes);
  const [edges, setEdges] = useState(initialEdges);
  //Boiler plate code for reactFlow
  const onNodesChange = useCallback(
    (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
    [setNodes]
  );
  const onEdgesChange = useCallback(
    (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
    [setEdges]
  );
  const onConnect = useCallback(
    (connection) => setEdges((eds) => addEdge(connection, eds)),
    [setEdges]
  );

  return (
    <Fragment>
      <Row>
        <Col lg={9}>
          <ReactFlow
            className="Canvas mt-1 border border-secondary rounded"
            nodes={nodes} //Node information is passed here
            edges={edges} //Edges information is passed here
            onNodesChange={onNodesChange}
            onEdgesChange={onEdgesChange}
            onConnect={onConnect}
            defaultEdgeOptions={defaultEdgeOptions}
            style={{ width: "100%", height: "80vh" }}
            fitView
            nodeTypes={nodeTypes}
          />
        </Col>
      </Row>
    </Fragment>
  );
}

export default Flow;

我在node.jsnode.js属性中添加了更多信息。它最终被初始化为空,但是这个模板应该有助于理解我是如何为node保存信息的。edge遵循react-flow文档中显示的标准格式。

代码语言:javascript
运行
复制
export default [
    // {
    //   id: '1',
    //   type: 'customNode',
    //   data: { label: 'Input Node', info: [{id:1, action:"Everything is burning"}, {id:2, action:"I'm fine"}], noOfActions:2 },
    //   position: { x: 250, y: 25 },
    // },
  ];

我希望这是有用的!

票数 0
EN

Stack Overflow用户

发布于 2022-09-13 03:45:37

你想要做的不仅仅是这些:

您可以在这里看到活生生的例子:https://codesandbox.io/s/dank-waterfall-8jfcf4?file=/src/App.js

基本上,你需要:

  • 从“react flow-renderer”导入useNodesState
  • 与节点的基本定义不同,您需要使用:const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  • 然后,必须定义onAdd,它如下所示:
代码语言:javascript
运行
复制
 const onAdd = useCallback(() => {
   const newNode = {
     id: getNodeId(),
     data: { label: `${state.name} (${state.age})` },
     position: {
       x: 0,
       y: 0 + (nodes.length + 1) * 20
     }
   };
   setNodes((nds) => nds.concat(newNode));
 }, [nodes, setNodes, state.name, state.age]);
  • 您可以包含编辑,非常类似的是:
代码语言:javascript
运行
复制
  const onEdit = () => {
    setNodes((nds) =>
      nds.map((node) => {
        if (node.id === editState.id) {
          node.data = {
            ...node.data,
            label: `${node.id} - ${editState.name} (${editState.age})`
          };
        }

        return node;
      })
    );
  };
  • 最后,绘制流程:<ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />

整个代码看起来如下:

代码语言:javascript
运行
复制
import React, { useState, useCallback } from "react";
import ReactFlow, {
  ReactFlowProvider,
  useNodesState,
  useEdgesState
} from "react-flow-renderer";

import "./styles.css";

const getNodeId = () => `randomnode_${+new Date()}`;

const initialNodes = [
  { id: "1", data: { label: "Node 1" }, position: { x: 100, y: 100 } },
  { id: "2", data: { label: "Node 2" }, position: { x: 100, y: 200 } }
];

const initialEdges = [{ id: "e1-2", source: "1", target: "2" }];

const FlowExample = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges] = useEdgesState(initialEdges);
  const [state, setState] = useState({ name: "", age: "" });

  const onAdd = useCallback(() => {
    const newNode = {
      id: getNodeId(),
      data: { label: `${state.name} (${state.age})` },
      position: {
        x: 0,
        y: 0 + (nodes.length + 1) * 20
      }
    };
    setNodes((nds) => nds.concat(newNode));
  }, [nodes, setNodes, state.name, state.age]);

  return (
    <div>
      Name:{" "}
      <input
        type="text"
        onChange={(e) => {
          setState((prev) => ({ ...prev, name: e.target.value }));
        }}
      />
      Age:{" "}
      <input
        type="text"
        onChange={(e) => {
          setState((prev) => ({ ...prev, age: e.target.value }));
        }}
      />
      <button onClick={onAdd}>add node</button>
      <div style={{ width: "500px", height: "500px" }}>
        <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
      </div>
    </div>
  );
};

export default () => (
  <ReactFlowProvider>
    <FlowExample />
  </ReactFlowProvider>
);

此外,通过编辑:

代码语言:javascript
运行
复制
import React, { useState, useCallback } from "react";
import ReactFlow, {
  ReactFlowProvider,
  useNodesState,
  useEdgesState
} from "react-flow-renderer";

import "./styles.css";

const getNodeId = () => `${String(+new Date()).slice(6)}`;

const initialNodes = [
  { id: "1", data: { label: "Node 1" }, position: { x: 100, y: 100 } },
  { id: "2", data: { label: "Node 2" }, position: { x: 100, y: 200 } }
];

const initialEdges = [{ id: "e1-2", source: "1", target: "2" }];

const FlowExample = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges] = useEdgesState(initialEdges);
  const [state, setState] = useState({ name: "", age: "" });
  const [editState, setEditState] = useState({ id: "", name: "", age: "" });

  const onEdit = () => {
    setNodes((nds) =>
      nds.map((node) => {
        if (node.id === editState.id) {
          node.data = {
            ...node.data,
            label: `${node.id} - ${editState.name} (${editState.age})`
          };
        }

        return node;
      })
    );
  };

  const onAdd = () => {
    const id = getNodeId();
    const newNode = {
      id,
      data: { label: `${id} - ${state.name} (${state.age})` },
      position: {
        x: 0,
        y: 0 + (nodes.length + 1) * 20
      }
    };
    setNodes((nds) => nds.concat(newNode));
  };

  return (
    <div>
      Name:{" "}
      <input
        type="text"
        onChange={(e) => {
          setState((prev) => ({ ...prev, name: e.target.value }));
        }}
      />
      Age:{" "}
      <input
        type="text"
        onChange={(e) => {
          setState((prev) => ({ ...prev, age: e.target.value }));
        }}
      />
      <button onClick={onAdd}>add node</button>
      <br />
      Id:{" "}
      <input
        type="text"
        onChange={(e) => {
          setEditState((prev) => ({ ...prev, id: e.target.value }));
        }}
      />
      Name:{" "}
      <input
        type="text"
        onChange={(e) => {
          setEditState((prev) => ({ ...prev, name: e.target.value }));
        }}
      />
      Age:{" "}
      <input
        type="text"
        onChange={(e) => {
          setEditState((prev) => ({ ...prev, age: e.target.value }));
        }}
      />
      <button onClick={onEdit}>Edit node</button>
      <div style={{ width: "500px", height: "500px" }}>
        <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
      </div>
    </div>
  );
};

export default () => (
  <ReactFlowProvider>
    <FlowExample />
  </ReactFlowProvider>
);

从文件中获得的一个更有帮助的例子是:

但是,您必须删除所有额外的信息(而且,您还可以使用它进行更深的处理!)

票数 4
EN

Stack Overflow用户

发布于 2022-11-28 10:01:47

公认的答案是修改组件的属性,而这不是反应方式。这段代码很容易被破解。还有其他将回调引入自定义节点的方法。

  1. 将回调放入节点的数据

这来自React文档:https://reactflow.dev/docs/examples/nodes/custom-node/

代码语言:javascript
运行
复制
    setNodes([
      ...
      {
        id: '2',
        type: 'selectorNode',
        data: { onChange: onChange, color: initBgColor },
      ...

缺点:在动态修改或创建新节点时,需要格外注意。

  1. 动态定义自定义类型

在这种方法中,将节点数据和行为关注点分开处理。

我使用TypeScript来显示我们沿途操作的数据类型。

首先,使用回调扩展自定义节点属性:

代码语言:javascript
运行
复制
import {NodeProps} from "react-flow-renderer/dist/esm/types/nodes";

// by default, custom node is provisioned with NodeProps<T>
// we extend it with additional property
export type CustomNodeProps = NodeProps<CustomData> & {
  onClick: (id: string) => void
}

function CustomNode(props: CustomNodeProps) {
  return <button onClick={() => props.onClick(props.id)}>Do it</button>
}

然后创建新的构造函数,该构造函数提供回调,并使用自传将其放入自定义节点映射中:

代码语言:javascript
运行
复制
function Flow() {
  const [graph, dispatchAction] = useReducer(...);
  ...

  // useMemo is neccessary https://reactflow.dev/docs/guides/troubleshooting/#it-looks-like-you-have-created-a-new-nodetypes-or-edgetypes-object-if-this-wasnt-on-purpose-please-define-the-nodetypesedgetypes-outside-of-the-component-or-memoize-them
  const nodeTypes = useMemo(() => {
    return {
      custom: (props: NodeProps<CustomData>) => {
        return CustomNode({...props, onClick: (id: string) => {
          dispatchAction({
            type: 'customNodeButtonClicked',
            nodeId: id,
          })
        }})
      }
    }
  }, [])

  return (
    <>
      <ReactFlow nodeTypes={nodeTypes} ... />
    </>
  );
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73508204

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档