React 的 createPortal 在 JSX 组件外部调用时不会更新 DOM。

huangapple go评论47阅读模式
英文:

React createProtal called outsite a JSX component not updating the DOM

问题

I am trying to render a dynamically generated react component in a react app using createPortal.
When I call createPortal from a class the component is not rendered.

Handler.ts the class that contains the business logic

export class Handler {
  private element: HTMLElement | null;

  constructor(selector: string) {
      this.element = document.getElementById(selector);
  }

  attachedEvent() {
      this.element?.addEventListener("mouseenter", () => {
          let cancel = setTimeout(() => {
              if (this.element != null)
                  this.attachUi(this.element)
          }, 1000)

          this.element?.addEventListener('mouseleave', () => {
              clearTimeout(cancel)
          })
      })
  }

  attachUi(domNode: HTMLElement) {
      createPortal(createElement(
          'h1',
          {className: 'greeting'},
          'Hello'
      ), domNode);
  }
}

Main.tsx the react component that uses Handler.ts

const handler = new Handler("test_comp");
export default function Main() {

  useEffect(() => {
      // @ts-ignore
      handler.useAddEventListeners();
  });
  return (
      <>
        <div id="test_comp">
          <p>Detect Mouse</p>
        </div>
      </>
  )
 }

However, when I replace the attachUi function with the function below, it works:

attachUi(domNode: HTMLElement) {
    const root = createRoot(domNode);
    root.render(createElement(
        'h1',
        {className: 'greeting'},
        'Hello'
    ));
  }
}

What am I missing?


<details>
<summary>英文:</summary>

I am trying to render a dynamically generated react component in a react app using createProtal. 
When I call createProtal from a class the component is not rendered.

`Handler.ts` the class the contains the business logic

    export class Handler {
      private element: HTMLElement | null;

      constructor(selector: string) {
          this.element = document.getElementById(selector);
      }

      attachedEvent() {
          this.element?.addEventListener(&quot;mouseenter&quot;, () =&gt; {
              let cancel = setTimeout(() =&gt; {
                  if (this.element != null)
                      this.attachUi(this.element)
              }, 1000)

              this.element?.addEventListener(&#39;mouseleave&#39;, () =&gt; {
                  clearTimeout(cancel)
              })
          })
      }

      attachUi(domNode: HTMLElement) {
          createPortal(createElement(
              &#39;h1&#39;,
              {className: &#39;greeting&#39;},
              &#39;Hello&#39;
          ), domNode);
      }
    }

`Main.tsx` the react component that uses `Handler.ts`

    const handler = new Handler(&quot;test_comp&quot;);
    export default function Main() {

      useEffect(() =&gt; {
          // @ts-ignore
          handler.useAddEventListeners();
      });
      return (
          &lt;&gt;
            &lt;div id=&quot;test_comp&quot;&gt;
              &lt;p&gt;Detect Mouse&lt;/p&gt;
            &lt;/div&gt;
          &lt;/&gt;
      )
     }

However when I repleace `attachUi` function with the function below it works 

     attachUi(domNode: HTMLElement) {
        const root = createRoot(domNode);
        root.render(createElement(
            &#39;h1&#39;,
            {className: &#39;greeting&#39;},
            &#39;Hello&#39;
        ));
      }

What am I missing?

</details>


# 答案1
**得分**: 1

React使用一种叫做Virtual DOM的东西只有包含在VDOM中的组件才会显示在屏幕上组件返回React能理解并包含在VDOM中的内容

`createPortal(...)` 返回的内容与 `&lt;SomeComponent ... /&gt;` 完全相同

所以如果你只是这样做`const something = &lt;SomeComponent /&gt;`而且你没有在任何地方使用这个变量那么它就不会被显示`createPortal` 也是一样的`const something = createPortal(...)`如果你想显示它只需在某处使用这个变量将它添加到VDOM中让一些你的组件返回它

你的结构是

    App
     -子组件
       -孙子组件
     -子组件2

而你的Portal在别的地方它没有附加到那个VDOM上如果你想要显示它你必须在那里包含它

在你下一个示例中使用`root.render`你创建了一个新的VDOM它与你的主VDOM分开这就是为什么它会被显示出来

<details>
<summary>英文:</summary>

React uses something called Virtual DOM. Only components that are included in that VDOM are displayed to the screen. A component returns something that React understands and includes to the VDOM.

`createPortal(...)` returns exactly the same as `&lt;SomeComponent ... /&gt;`

So if you just do: `const something = &lt;SomeComponent /&gt;` and you don&#39;t use that variable anywhere, you can not display it. The same is with `createPortal`. `const something = createPortal(...)`. Just use that variable somewhere if you want to display it. Add it to VDOM, let some of your components return it.


Your structure is

    App
     -children
       -grand children
     -children2


And your portal is somewhere else, that is not attached to that VDOM. You have to include it there, if you want to be displayed.

In your next example using root.render you create new VDOM. It is separated from your main one. This is why it is displayed

</details>



huangapple
  • 本文由 发表于 2023年2月6日 18:23:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75360043.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定