Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D3 Force example nodes not draggable #429

Open
moklick opened this issue Jul 10, 2024 · 15 comments
Open

D3 Force example nodes not draggable #429

moklick opened this issue Jul 10, 2024 · 15 comments
Assignees
Labels
react-docs Issues belonging to the reactflow website

Comments

@moklick
Copy link
Member

moklick commented Jul 10, 2024

While the simulation is running, the nodes are not draggable https://reactflow.dev/learn/layouting/layouting#d3-force

@moklick moklick added the react-docs Issues belonging to the reactflow website label Jul 10, 2024
@hayleigh-dot-dev
Copy link
Contributor

They are draggable for me (Mac, safari 17.5), although performance is notably worse than it used to be.

@BensEye
Copy link

BensEye commented Jul 21, 2024

They are only draggable after the simulation stops running, for me.

@buchananwill
Copy link
Contributor

I have worked through this problem myself with V11. It was quite tricky to get all the interactivity to play ball. I'm about to update v12, so once I've checked that none of my solution breaks I will post a link.

@buchananwill
Copy link
Contributor

I've copied over the simplest solution I found to get the node dragging to function correctly with d3 force layouting. Here's my fork of the example repo:

https://github.com/buchananwill/quizzical-lamarr-tv8srd.git

The gist of the solution is to use the callbacks for onNodeDragStart/onNodeDrag/onNodeDragStop to assign the currently dragging node to a ref:

    const draggingNodeRef = useRef(undefined);
    const [initialized, {toggle, isRunning}] = useLayoutedElements(draggingNodeRef);

    const onNodeDragStart = useCallback(
        (_event, node) => {
            draggingNodeRef.current = {...node};
        },
        []
    );

    const onNodeDragStop = useCallback(() => {
        draggingNodeRef.current = undefined;
    }, []);

    const onNodeDrag = useCallback(
        (_event, node) => {
            draggingNodeRef.current = {...node};
        },
        []
    );

This ref is passed into the useLayoutedElements hook, so the memoized function can always guarantee to see a non-stale reference. I made a few other minor changes to tidy up some janky behaviour, like copying the xyflow positions back onto the sim nodes before restarting the sim - otherwise the nodes forget where you might have dragged them to and hop back to the last position the sim remembers.

Feel free to copy my change/fork back into the example version for the web docs.

@moklick
Copy link
Member Author

moklick commented Sep 11, 2024

Thanks for sharing your solution @buchananwill ! We will have a look

@lihebi
Copy link

lihebi commented Sep 22, 2024

Some observations on the website example (https://reactflow.dev/learn/layouting/layouting#d3-force):

  1. the nodes are draggable in Safari
  2. the nodes are not draggable in Chrome/Firefox
  3. (very weird) if you open the dev tool on Chrome, the nodes are draggable

All above are after starting force simulation, i.e., auto-layout in effect.

@buchananwill
Copy link
Contributor

buchananwill commented Sep 22, 2024 via email

@lihebi
Copy link

lihebi commented Sep 23, 2024

Thanks @buchananwill , I actually did try the codesandbox link on your repo. The behavior is exactly the same as the website example (all 1,2,3 above). I'm surprised that it works on your side. I'm on MacOS 15.0, Chrome 128.0.6613.138 (Official Build).

@buchananwill
Copy link
Contributor

Ah, you're right. I checked against my main project I copied from and I'd missed one detail: the current drag position needs to come directly from the ref, not the scoped list of nodes (which is stale).

      getNodes().forEach((node, i) => {
        const dragging = draggingNodeRef?.current?.id === node.id;

        // Setting the fx/fy properties of a node tells the simulation to "fix"
        // the node at that position and ignore any forces that would normally
        // cause it to move.
        if (dragging) {
          // These two lines were wrong.
          nodes[i].fx = draggingNodeRef?.current?.position.x; 
          nodes[i].fy = draggingNodeRef?.current?.position.y;
        } else {
          delete nodes[i].fx;
          delete nodes[i].fy;
        }
      });

@buchananwill
Copy link
Contributor

Please try the codesandbox again and let me know if it works for you now!

@lihebi
Copy link

lihebi commented Sep 23, 2024

Awesome, it works! Thanks for the quick response and debugging.

@buchananwill
Copy link
Contributor

buchananwill commented Sep 23, 2024 via email

@hayleigh-dot-dev
Copy link
Contributor

Hey @buchananwill thank you so much for investigating! I can confirm that your repo/sandbox works outside of safari: it's so strange that it works at all in safari 🧐

We're in the process of reworking how we host our examples (#501) but I'll make sure these changes are added and we'll shout you out when things go live 💕. Until then I'll leave the issue open in case anyone else needs to find it.

@buchananwill
Copy link
Contributor

buchananwill commented Oct 1, 2024 via email

@buchananwill
Copy link
Contributor

buchananwill commented Oct 16, 2024

I've keep running into this issue even though I have a mostly working graph setup. Today I found that adding the useNodesData() hook into one of the node component implementations I have, breaks the dragging behaviour again. I worked around it by instead subscribing to the higher up context I am wrapping my xyflow setup in.

const { nodes } = useNodeContext<OrganizationDto>();

This doesn't trigger re-renders from selection/movement updates, because it's the external data, rather than the full UI state. So, it'll re-render if a node is added, or the "business" data on any node changes, but not the UI-level state changes.

Does this give any clues about why the dragging behaviour would break in the first place?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
react-docs Issues belonging to the reactflow website
Projects
None yet
Development

No branches or pull requests

5 participants