Update name of selected node from sidebar in React Flow

Viewed 1038

I am very new to react.js and JavaScript, so apologies in advance. I am trying to make a very concise diagramming tool in my webpage, where the user can create their own use case diagram.

I am looking at these examples:

https://reactflow.dev/examples/drag-and-drop/

https://reactflow.dev/examples/update-node/

I got the drag and drop to work, but am having a hard time applying the update node feature. My final goal is to let the user select a node, and update its name via input at the sidebar.

I started with adding const onElementClick = (event, element) => event.dataTransfer.getData("label"); to the main file to get the label from the sidebar, and am trying to send back the changed label by adding the block code below.

<label> label: </label>
<input value={nodeName} onChange={(evt) => setNodeName(evt.target.value)} />

Because their example on https://reactflow.dev/examples/update-node/ has everything in a single file, and am not able to update the node's name with selection, I am just a bit unsure how to proceed from here.

Any help would be sincerely appreciated, as a beginner in react. Again, apologies and thanks in advance!

main.js

import Sidebar from "./Sidebar";

const initialElements = [
  {
    id: "1",
    type: "input",
    data: { label: "User" },
    position: { x: 300, y: 150 },
  },

  {
    id: "2",
    type: "default",
    data: { label: "Use Case" },
    position: { x: 700, y: 250 },
  },

  {
    id: "3",
    type: "output",
    data: { label: "Database" },
    position: { x: 1100, y: 350 },
  },
];

let id = 0;
const getId = () => `dndnode_${id++}`;

export const Tester = () => {
  const reactFlowWrapper = useRef(null);
  const [reactFlowInstance, setReactFlowInstance] = useState(null);
  const [elements, setElements] = useState(initialElements);
  const [nodeName, setNodeName] = useState("Node 1");
  const [nodeBg, setNodeBg] = useState("#eee");
  const [nodeHidden, setNodeHidden] = useState(false);

  const onElementClick = (event, element) => event.dataTransfer.getData("click", element);

  const onConnect = (params) => setElements((els) => addEdge(params, els));
  const onElementsRemove = (elementsToRemove) =>
    setElements((els) => removeElements(elementsToRemove, els));

  const onLoad = (_reactFlowInstance) => setReactFlowInstance(_reactFlowInstance);

  const onDragOver = (event) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
  };

  const onDrop = (event) => {
    event.preventDefault();

    const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
    const type = event.dataTransfer.getData("application/reactflow");
    const label = event.dataTransfer.getData("label");
    const position = reactFlowInstance.project({
      x: event.clientX - reactFlowBounds.left,
      y: event.clientY - reactFlowBounds.top,
    });
    const newNode = {
      id: getId(),
      type,
      position,
      data: { label: label },
    };

    setElements((es) => es.concat(newNode));
  };

  return (
    <div className="dndflow">
      <ReactFlowProvider>
        <Box
          className="reactflow-wrapper"
          ref={reactFlowWrapper}
          style={{ height: 600 }}
          sx={{ display: "flex", justifyContent: "space-between", justifyContent: "center" }}
        >
          <ReactFlow
            elements={elements}
            onConnect={onConnect}
            onElementsRemove={onElementsRemove}
            onLoad={onLoad}
            onDrop={onDrop}
            onDragOver={onDragOver}
            onElementClick={onElementClick}
          >
            <Controls />
            <MiniMap />
            <Background variant="lines" size={1.5} gap={20} color="#eee" />
          </ReactFlow>
          <Box sx={{ ml: 10 }}>
            <Sidebar />
          </Box>
        </Box>
      </ReactFlowProvider>
    </div>
  );
};

Sidebar.js

export default () => {
  const onDragStart = (event, nodeType, label) => {
    event.dataTransfer.setData("application/reactflow", nodeType);
    event.dataTransfer.setData("label", label);
    event.dataTransfer.effectAllowed = "move";
  };

  const changeNodeName = (event, nodetype, label) => {
    //event.dataTransfer.getData()
    const [nodeName, setNodeName] = useState("Node 1");
  };

  return (
    <aside>
      <Box
        sx={{
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <div className="description">Drag nodes to the pane.</div>
        <Box
          className="dndnode input"
          onDragStart={(event) => onDragStart(event, "input", "User")}
          draggable
          sx={{
            mt: 2,
            border: 1,
            borderColor: "blue",
            borderRadius: 0.5,
            width: 200,
            height: 50,
            textAlign: "center",
            py: 1.4,
            "&:hover": {
              opacity: 0.5,
            },
          }}
        >
          User
        </Box>
        <Box
          className="dndnode"
          onDragStart={(event) => onDragStart(event, "default", "Use Case")}
          draggable
          sx={{
            mt: 2,
            border: 0.5,
            borderColor: "black",
            borderRadius: 0.5,
            width: 200,
            height: 50,
            textAlign: "center",
            py: 1.4,
            "&:hover": {
              opacity: 0.5,
            },
          }}
        >
          Use Case
        </Box>
        <Box
          className="dndnode output"
          onDragStart={(event) => onDragStart(event, "output", "Database")}
          draggable
          sx={{
            mt: 2,
            border: 0.5,
            borderColor: "red",
            borderRadius: 0.5,
            width: 200,
            height: 50,
            textAlign: "center",
            py: 1.4,
            "&:hover": {
              opacity: 0.5,
            },
          }}
        >
          Database
        </Box>
      </Box>
    </aside>
  );
};

1 Answers

I get the same problem as you, but after a while, I solve this problem after reading it many times in the docs https://reactflow.dev/docs/api/react-flow-props/, you can use onNodeClick(event: React.MouseEvent, node: Node) and set a state like show-sidebar that will change every time u click on the nodes set-Show-Sidebar( !show-sidebar), so when it gets true u send the node-id to Sidebar change it and receiver it, change it with onChange function then update nodes with nodes.map((node)=>{node.id === id ? {...node, label:newName } : node})

Related