【问题标题】:Popover does not show up in React弹出框没有出现在 React 中
【发布时间】:2021-11-11 08:21:29
【问题描述】:

我正在尝试使用 Treeviz 依赖项创建一个 web 应用程序。目标是在树的每个节点上放置一个弹出按钮,如果用户点击按钮,他/她可以看到节点的描述,之后它应该是可编辑的。我尝试了很多方法,但对我来说 popover 在 React 中不起作用。

有一个我想做的例子。您可以看到我必须将 React 组件插入 HTML,因此我使用的是 renderToString。您只需要查看树的 renderNode 属性。我在 renderNode 中引用 React 组件,例如:${tooltip} ${popover}。

import React from "react";
import { TreevizReact } from "treeviz-react";
import { renderToString } from "react-dom/server";
const data_1 = [
  {
    id: 1,
    text_1: "Chaos",
    description: "Void",
    father: null,
    color: "#FF5722"
  },
  {
    id: 2,
    text_1: "Tartarus",
    description: "Abyss",
    father: 1,
    color: "#FFC107"
  },
  { id: 3, text_1: "Gaia", description: "Earth", father: 1, color: "#8BC34A" },
  { id: 4, text_1: "Eros", description: "Desire", father: 1, color: "#00BCD4" }
];

export const App = () => {
  return (
    <div style={{ marginLeft: 10 }}>
      <div style={{ display: "flex" }}>
        <TreevizReact
          data={data_1}
          relationnalField={"father"}
          nodeWidth={120}
          nodeHeight={80}
          areaHeight={500}
          areaWidth={1000}
          mainAxisNodeSpacing={2}
          secondaryAxisNodeSpacing={2}
          linkShape={"quadraticBeziers"}
          renderNode={(data) => {
            const nodeData = data.data;
            const settings = data.settings;
            let result = "";
            const tooltip = renderToString(
              <strong
                data-toggle="tooltip"
                data-placement="top"
                title={nodeData.description}
              >
                {nodeData.text_1}
              </strong>
            );
            const popover = renderToString(
              <button
                type="button"
                className="btn btn-secondary"
                data-container="body"
                data-toggle="popover"
                data-placement="top"
                data-content={nodeData.description}
              >
                Popover on top
              </button>
            );
            if (data.depth !== 2) {
              result = `<div className="box" 
              style='cursor:pointer;height:${settings.nodeHeight}px; width:${settings.nodeWidth}px;display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:${nodeData.color};border-radius:5px;'>
              <div>
          ${tooltip}
          ${popover}
          </div></div>`;
            } else {
              result = `<div className="box" style='cursor:pointer;height:${
                settings.nodeHeight
              }px; width:${
                settings.nodeHeight
              }px;display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:${
                nodeData.color
              };border-radius:${settings.nodeHeight / 2}px;'><div><strong>
          ${nodeData.text_1}
          </strong></div></div>`;
            }
            return result;
          }}
          duration={600}
          isHorizontal
          linkWidth={(node) => 10}
        />
      </div>
    </div>
  );
};
export default App;

工具提示正在运行,但弹出框未显示。

你可以试试:https://codesandbox.io/s/zealous-orla-4bq5f?file=/src/App.js

也试过了

const popover = renderToString(
              <Popup
                trigger={<button> Trigger</button>}
                position="right center"
              >
                <form onSubmit={saveHandler}>
                  <ContentEditable
                    html={text.current}
                    onChange={handleChange}
                  />
                  <button type="submit">Save</button>
                  <button>Cancel</button>
                </form>
              </Popup>
const popoverContent = (
              <Popover id="popover-basic">
                <Popover.Header as="h3">Popover right</Popover.Header>
                <Popover.Body>
                  And here's some <strong>amazing</strong> content. It's very
                  engaging. right?
                </Popover.Body>
              </Popover>
            );

            const popover = renderToString(
              <OverlayTrigger
                trigger="click"
                placement="right"
                overlay={popoverContent}
              >
                <Button variant="success">Click me to see</Button>
              </OverlayTrigger>
            );

它们都不适合我。

【问题讨论】:

  • 经过大量的尝试,似乎你想要实现的东西是不可能的。因为TreevizReact 有自己的onNodeClick,它包装了每个节点的内容。此外renderNode 方法将内容呈现为无法嵌入bootstrap popover 组件的字符串。结果组件无法正常工作。

标签: javascript html reactjs popover treeviz-react


【解决方案1】:

可能您的方法不起作用,因为树中的 dom 元素是动态创建的,并且bootstrap doesn't set them up。

一种更反应的方式是使用 react-bootstrap 库并管理状态中的每个 UI 方面。为了实现工具提示,Overlay 组件实际上是一个名为 target 的道具,它允许您更改显示工具提示的元素。

    <Overlay target={tooltipTarget} show={showTooltip} placement="right">
      {(props) => (
        <Tooltip id="overlay-example" {...props}>
          {tooltipNode?.data.text_1}
        </Tooltip>
      )}
    </Overlay>

那么您只需要在TreevizReact 组件中的onNodeMouseEnter 和onNodeMouseLeave 处理程序中管理所有这些状态。

        onNodeMouseEnter={(_event, node) => {
          const t = document.querySelector(`#node-${node.id}`);
          setTooltipTarget(t);
          setShowTooltip(true);
          setTooltipNode(node);
        }}
        onNodeMouseLeave={(_event, node) => {
          setTooltipTarget(null);
          setShowTooltip(false);
          setTooltipNode(null);
        }}

弹出窗口遵循与另一组状态相同的逻辑。

  <div ref={ref}>
    <Overlay
      show={!!selectedNode.current}
      target={target}
      placement="bottom"
      container={ref.current}
      containerPadding={20}
    >
      <Popover id="popover-contained">
        {/* hack to avoid weird blinking (due mutable state) */}
        {!!selectedNode.current && (
          <>
            some form based on node data
            {JSON.stringify(selectedNode.current?.data)}
          </>
        )}
      </Popover>
    </Overlay>
  </div>

在 onNodeClick 处理程序中

        onNodeClick={(_event, node) => {
          const t = document.querySelector(`#node-${node.id}`);
          // unselect if already selected
          if (selectedNode.current?.id === node.id) {
            selectedNode.current = null;
            setTarget(null);
          } else {
            selectedNode.current = node;
            setTarget(t);
          }
        }}

您可能会注意到这次我通过 ref (selectedNode.current) 使用了可变变量,由于某种原因使用 state 导致了一些问题,可能是由于 D3 和 react 具有不同的渲染周期,您可能会遇到的原因此实现中的其他一些故障。

https://codesandbox.io/s/quizzical-buck-slofh?file=/src/App.js

【讨论】:

  • 谢谢!你能解释一下你在 onNodeClick 道具中用 _event 跳过了什么吗?它如何改变节点的属性?
  • @Phaki 看起来第一个参数是某种事件对象,这就是为什么我必须添加它来访问第二个参数,即节点数据
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-28
相关资源
最近更新 更多