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

Load node data on hover #529

Open
happylolonly opened this issue Jul 22, 2024 · 2 comments
Open

Load node data on hover #529

happylolonly opened this issue Jul 22, 2024 · 2 comments

Comments

@happylolonly
Copy link

What is correct way to load node data on hover?

@hyeonbeomsong
Copy link

hyeonbeomsong commented Oct 18, 2024

onNodeHover?: (node: NodeObject | null, previousNode: NodeObject | null) => void;

@happylolonly You can get the value of the node from the onNodeHover function.

In the case of react, <ForceGraph onNodeHover={(node) => console.log("hovering node", node) } />
Is this the right question for you?

@Not-yourCoder
Copy link

Not-yourCoder commented Nov 26, 2024

Suppose i want to render a tooltip displaying the node details on hover, how would i do it. Also for some reason when i hover over the node, the entire graph re renders. It keeps re rendering until unless i remove my mouse from the node.
the code ;
` import React, { useMemo, useState, useCallback, useRef } from 'react';
import ForceGraph2D from 'react-force-graph-2d';
import { graphData } from './data';
import { generateNodesForForceGraph } from './utils/nodeGenerator';
import { generateEdgesForForceGraph } from './utils/edgeGenerator';

const HighlightGraph: React.FC = () => {
// Generate nodes and links
const nodes = generateNodesForForceGraph(graphData);
const links = generateEdgesForForceGraph(graphData);

// Memoize node and link cross-referencing
const { nodesWithNeighbors, enhancedLinks } = useMemo(() => {
    // Cross-link node objects
    const nodesMap = new Map(nodes.map(node => [node.id, { ...node, neighbors: [], links: [] }]));

    const enhancedLinks = links.map(link => {
        const sourceNode = nodesMap.get(link.source);
        const targetNode = nodesMap.get(link.target);

        if (sourceNode && targetNode) {
            sourceNode.neighbors.push(targetNode);
            targetNode.neighbors.push(sourceNode);
            sourceNode.links.push(link);
            targetNode.links.push(link);
        }

        return {
            ...link,
            source: sourceNode,
            target: targetNode
        };
    });

    return {
        nodesWithNeighbors: Array.from(nodesMap.values()),
        enhancedLinks
    };
}, [nodes, links]);

// State for highlighting with useRef to prevent unnecessary re-renders
const highlightNodesRef = useRef(new Set());
const highlightLinksRef = useRef(new Set());
const [hoverNode, setHoverNode] = useState<any>(null);

// Optimized node hover handler
const handleNodeHover = useCallback((node) => {
    // Prevent unnecessary state updates if the hovered node is the same
    if (node === hoverNode) return;

    console.log("node hovered", node)
    // Clear previous highlights
    highlightNodesRef.current.clear();
    highlightLinksRef.current.clear();

    if (node) {
        // Add hovered node
        highlightNodesRef.current.add(node);

        // Add neighbors and their links
        node.neighbors.forEach(neighbor => {
            highlightNodesRef.current.add(neighbor);
        });
        node.links.forEach(link => {
            highlightLinksRef.current.add(link);
        });
    }

    // Update hover node state
    setHoverNode(node);
}, [hoverNode]);

// Link hover handler
const handleLinkHover = useCallback((link) => {
    // Clear previous highlights
    highlightNodesRef.current.clear();
    highlightLinksRef.current.clear();

    if (link) {
        highlightLinksRef.current.add(link);
        highlightNodesRef.current.add(link.source);
        highlightNodesRef.current.add(link.target);
    }

    // Reset hover node
    setHoverNode(null);
}, []);

// Optimized ring painting with useCallback
const paintRing = useCallback((node, ctx) => {
    // Only paint for highlighted nodes
    if (highlightNodesRef.current.has(node)) {
        ctx.beginPath();
        ctx.arc(node.x, node.y, 4 * 1.4, 0, 2 * Math.PI, false);
        ctx.fillStyle = node === hoverNode ? 'red' : 'orange';
        ctx.fill();
    }
}, [hoverNode]);

// Full node pointer area
const nodePointerAreaPaint = useCallback((node, color, ctx) => {
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(node.x, node.y, 4 * 1.4, 0, 2 * Math.PI, false);
    ctx.fill();
}, []);

console.log("Node paint", nodePointerAreaPaint)
return (
    <ForceGraph2D
        graphData={{
            nodes: nodesWithNeighbors,
            links: enhancedLinks
        }}
        nodeRelSize={4}
        autoPauseRedraw={false}

        nodePointerAreaPaint={nodePointerAreaPaint}
        // Link styling
        linkWidth={link => highlightLinksRef.current.has(link) ? 5 : 1}
        linkDirectionalParticles={4}
        linkDirectionalParticleWidth={link => highlightLinksRef.current.has(link) ? 4 : 0}

        // Node styling
        nodeCanvasObjectMode={node => highlightNodesRef.current.has(node) ? 'before' : undefined}
        nodeCanvasObject={paintRing}

        // Interaction handlers
        onNodeHover={handleNodeHover}
        onLinkHover={handleLinkHover}
    />
);

};

export default HighlightGraph;`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants