1

I am using React d3 tree with a custom node element. I am trying to show some text on hovering a node, but it only works the first time I hover over one, then the mouse.currentTarget seems to be null each time I hover a node, so documentation.show won't be set to true. The useMemo is used so that the whole component won't rerender each time I hover over a node (because of documentation state change).

import Tree, { TreeNodeDatum } from "react-d3-tree";

const [documentation, setDocumentation] = useState({
    x: 0,
    y: 0,
    show: false,
    text: "test text.",
  });

const TreeMemo = useMemo(() => {

    const renderCustomNode = ({
      nodeDatum,
      toggleNode,
    }: {
      nodeDatum: TreeNodeDatum;
      toggleNode: () => void;
    }) => (
      <g>
        <circle
          r="15"
          onMouseEnter={(mouse) =>
            setDocumentation((prevState) => {
              return mouse.currentTarget
                ? {
                    x:
                      mouse.clientX -
                      mouse.currentTarget.getBoundingClientRect().right,
                    y:
                      mouse.clientY -
                      mouse.currentTarget.getBoundingClientRect().top,
                    show: true,
                    text: prevState.text,
                  }
                : prevState;
            })
          }
          onMouseLeave={() => {
            setDocumentation((prevState) => {
              return {
                ...prevState,
                show: false,
              };
            });
          }}
          onClick={toggleNode}
        />
        <text fill="black" strokeWidth="1" x="20">
          {nodeDatum.name}
        </text>
        {nodeDatum.attributes?.department && (
          <text fill="black" x="20" dy="20" strokeWidth="1">
            Department: {nodeDatum.attributes?.department}
          </text>
        )}
      </g>
    );
    
    return (
      <Tree
        key={triggerRecenter}
        data={orgChart}
        translate={treeTranslate}
        dimensions={
          treeContainerRef.current
            ? {
                height: treeContainerRef.current.getBoundingClientRect().height,
                width: treeContainerRef.current.getBoundingClientRect().width,
              }
            : undefined
        }
        renderCustomNodeElement={(rd3tProps) =>
          renderCustomNode({ ...rd3tProps })
        }
        orientation="vertical"
      />
    );
  }, [triggerRecenter, treeTranslate]);

Adding documentation state in useMemo deps makes it work, but triggers rerender of the whole component which is not wanted. Also using onMouseMove seems to be working, but it makes the position change.

2
  • Have you tried "reverse" engineering it? Does it work without useMemo? If it does, then the issue is with your component structure.
    – JSEvgeny
    Commented Jul 7 at 13:28
  • Without useMemo it works yes, but it triggers the rerender of the whole component, which I don't want. How would I go about reverse engineering it? What could be wrong in this case with my component structure?
    – bogdmu00
    Commented Jul 7 at 13:34

1 Answer 1

0

Solved by changing onMouseEnter handler to below. Seems this prevents currentTarget to be null from what I understand, if I am wrong correct me.

     onMouseEnter={(event) => {
        const { clientX, clientY, currentTarget } = event;
        const { right, top } = currentTarget.getBoundingClientRect();

        setDocumentation((prevState) => {return {
          x: clientX - right,
          y: clientY - top,
          show: true,
          text: prevState.text,
        }});
      }}

Not the answer you're looking for? Browse other questions tagged or ask your own question.