xyflow / xyflow

React Flow | Svelte Flow - Powerful open source libraries for building node-based UIs with React (https://reactflow.dev) or Svelte (https://svelteflow.dev). Ready out-of-the-box and infinitely customizable.
https://xyflow.com
MIT License
21.54k stars 1.43k forks source link

[v12.next15] updateNodeData on a node triggers a replace on all nodes #4197

Closed bogris closed 3 weeks ago

bogris commented 3 weeks ago

Describe the Bug

when calling updateNodeData, all nodes are replaced.

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

attached the code that will reproduce the bug. on next.11 it works as expected:

Screenshot 2024-04-22 at 20 56 37

on next.14 / 15 it replaces all the nodes. with replace on true or false:

Screenshot 2024-04-22 at 21 00 17

used code:

'use client';

import { Button } from '@radix-ui/themes';
import {
  Background,
  Node,
  NodeProps,
  ReactFlow,
  ReactFlowProvider,
  useNodesState,
  useReactFlow,
  MiniMap,
  OnNodesChange,
  useNodeId,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { FC } from 'react';

const CustonNode: FC<NodeProps> = (props) => {
  const { id } = props;
  return (
    <div className="grid gap-2 rounded-lg bg-blue-200 p-3 text-blue-950">
      <span>{id}</span>
      <SetDataButton />
    </div>
  );
};

const SetDataButton = () => {
  const { updateNodeData } = useReactFlow();
  const nodeId = useNodeId();
  const replace: boolean = nodeId === '1';
  const onClick = () => {
    console.log(
      `clicked update data on node: ${nodeId}, with replace: ${replace}`,
    );
    nodeId &&
      updateNodeData(
        nodeId,
        { label: `label: ${nodeId}` },
        {
          replace,
        },
      );
  };
  return <Button onClick={onClick}>Update Data</Button>;
};

const nodeTypes = {
  custom: CustonNode,
};

const initialNodes: Node[] = [
  {
    id: '1',
    type: 'custom',
    data: { label: 'node 1' },
    position: { x: 200, y: 200 },
    width: 200,
    height: 100,
  },
  // default node
  {
    id: '2',
    type: 'custom',
    data: { label: 'node 2' },
    position: { x: 200, y: 300 },
    width: 200,
    height: 100,
  },
  {
    id: '3',
    type: 'custom',
    data: { label: 'node 3' },
    position: { x: 200, y: 400 },
    width: 200,
    height: 100,
  },
];

export default function FlowCore() {
  const [nodes, setNodes, onNodesChangeMaster] = useNodesState(initialNodes);

  const onNodesChange: OnNodesChange = (changes) => {
    console.log({ changes });
    onNodesChangeMaster(changes);
  };

  return (
    <div className="fixed inset-0 top-10">
      <ReactFlowProvider>
        <ReactFlow {...{ nodeTypes, nodes, onNodesChange }}>
          <Background />
          <MiniMap />
        </ReactFlow>
      </ReactFlowProvider>
    </div>
  );
}

Expected behavior

only replace the node targeted by updateNodeData

Screenshots or Videos

No response

Platform

Additional context

PS: i know I mentioned this in one of my other reports, but it was a messy report.

I hope this one will help you in isolating the bug better.

Final note: I also observed that between the 2 versions, there is an extra select change event fired. I did't changed the code between package updates.

moklick commented 3 weeks ago

Thanks! This is fixed in 12.0.0-next.16.