這幾天專案要處理非常麻煩的畫面,子層被父層 overflow: hidden 加 postion relative 切到部分展開元件,突然想到之前同事提到新功能Portals,Portals能夠將元件向任意dom節點依附渲染,非常酷炫的功能,想說就順便來研究一下React Portals。

這邊會介紹兩個範例,頁面上渲染元件,還有渲染元件到window open。

React fragment React Portals

react

React Portals rule

要用React Portals必須遵照規定的格式 ReactDOM.createPortal(element,container),第一個參數要傳遞需要渲染的物件,第二個參數container,建立document.createElement('div') 作為render的container。

1
ReactDOM.createPortal(element,container)

會需要用到兩個lifeCycle,第一個是 componentDidMount react元件render後調用,另一個則是 componentWillUnmount 在元件被移除後會調用。

再來在componentDidMount上加入要渲染元素的節點,讓這個component render後調用appendChild,讓dom依附渲染上去。如果要移除渲染的話,則是控制component 移除觸發componentWillUnmount,再調用removeChild就完成了移除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// create component
class Portal extends React.Component {
constructor(props) {
super(props);
// create element as render container element
this.el = document.createElement('div');
}

componentDidMount() {
// select element to render
const elementId = document.getElementById('elementId');
elementId.appendChild(this.el);
}

componentWillUnmount() {
// select element to remove
const elementId = document.getElementById('elementId');
elementId.removeChild(this.el);
}

render() {
// first argument as element or string or fragment
// second argument as render container
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}

Portals example

聽起來可能有點抽象,實際來寫個範例看看。點擊左邊的按鈕是使用Portals顯示圖片,右邊的則是一般父子層顯示方法。

會發現左邊用Portals顯示,image元件會打印到section外,避免右邊的一般父子渲染方法,在section內被overflow hidden蓋住。

Portals example Code

Portals open window example

Portals 也可以打印到 window open 頁面的節點上。這邊範例是使用了 open window,再將元件透過 document.body.appendChild 渲染到開啟的視窗上。這邊是看到medium的範例,是利用setInterval 每秒執行一次 setState,處理 小視窗 更新 parent的視窗,有試驗過 postMessage 沒效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Portal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement("div");
this.Window = null;
}

componentDidMount() {
this.Window = window.open("","","width=800,height=500");
this.Window.document.body.appendChild(this.el);
}

componentWillUnmount() {
this.Window.close();
}

render() {
return ReactDOM.createPortal(
this.props.children,
this.el
);
}
}

Portals open window Code