---
# System prepended metadata

title: 'React Portal, 在 root 以外掛載元件'
tags: [React, ReactDom, Portal]

---

# React Portal, 在 root 以外掛載元件

###### tags: `React` `Portal` `ReactDom`

> React 提供了 Portal 方法，讓 children 可以 render 到 parent component DOM 樹以外的 DOM 節點。

像是以下範例
```
...
return  (
    <React.Fragment>
        <MyModal />
        <MyForm />
    </React.Fragment>
)
...
```
大至會產出以下 Real DOM
```
<section>
    <h2>Some Other content</h2>
    <div class='my-modal'>
        <h2>My Modal Title</h2>
    </div>
    <form>
        <label>Username</label>
        <input type='text' />
    </form>
</section>
```

但當 Modal 元件位置越來越深時，就會產生一些問題，在 HTML 語意上 Modal 作用使覆蓋整個頁面，嵌套在在某個元件中是較不理想的！

### 要如何改變掛載的節點呢？

到 public/index.html 找到 id 為 root 的 DOM，並在前方新增 `modal-root` & `backdrop-root` id

```
<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="backdrop-root"></div>
    <div id="modal-root"></div>
    <div id="root"></div>
    ...
</body>
```


到 ErrorModal 元件中有 `backdrop` 以及 `modal` 兩個平行的 div
```
const ErrorModal = (props) => {
  return (
    <>
      <div className={classes.backdrop} />
      <Card className={classes.modal}>
        ....
      </Card>
    </>
  );
};

export default ErrorModal;
```

將 `backdrop` 以及 `modal` 分開定義變數
#### 並且使用 `ReactDOM.createPortal()` 掛載至指定 DOM

```
// ReactDOM
import ReactDOM from "react-dom";

// Backdrop
const Backdrop = (props) => {
  return <div className={classes.backdrop} />;
};

// ModalOverlay
const ModalOverlay = (props) => {
  return (
    <Card className={classes.modal}>
      ...
    </Card>
  );
};

// 掛載節點
const ErrorModal = (props) => {
  return (
    <>
      {ReactDOM.createPortal(
        <Backdrop />,
        document.getElementById("backdrop-root")
      )}
      {ReactDOM.createPortal(
        <ModalOverlay />,
        document.getElementById("modal-root")
      )}
    </>
  );
};

export default ErrorModal;

```

到 chrome 觀察
可以發現剛剛新增的兩個節點裡面都有指定的元件在內了！

![](https://i.imgur.com/JVUSDWG.png)

觀察入口 index.js，使用 `ReactDOM.render` 訪法將 JSX 渲染至 root DOM 上，而 `React.createPortal` 也是使用差不多的邏輯
```
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
```

:::info
差別是我們不是將 ErrorModal 元件另外在另一個 DOM 上做渲染，而是改變已渲染完成的元件掛載的節點。
:::
