Portal 是 React 提供來讓子元件可以在父元件外渲染的一種方式,官網文件有提到一個經典案例,就是當父元件有著 overflow: hidden
或是 z-index
的 CSS 屬性,但我們需要讓子元件在視覺上能夠呈現在父元件外,就需要使用 Portal,如下圖,點擊父元件中的按鈕後彈出子元件,由於父元件有 overflow: hidden
屬性,子元件是不會顯示在父元件外,這時候就可以使用 Portal。
Portal 是透過 ReactDOM.createPortal(child, container)
這個方法來實現,child
和 container
兩個參數分別有以下的用途
child
是我們要插入的 React DOM,它可以是 JSX 或是字串。container
是我們要插入的地方,它必須是真實存在的 DOM
。 react-dom
引用一個函式 createPortal
,在 return
後面使用這函式去渲染。下面的例子我們在 createPortal
中的第一個參數是要插入 div
和文字,第二個參數是插入的位置,範例中是 body
。接著在 UserProfile
中插入 TestPortal
元件即可在畫面上渲染出來,如下面的圖片。import { createPortal } from 'react-dom';
class TestPortal extends React.Component {
render () {
return createPortal (
<div>
test portal
</div>,
document.body
);
}
}
class UserProfile extends React.Component {
render() {
return (
<div>
test component
<TestPortal />
</div>
);
}
}
但是要注意 container
必須是真正的 DOM
,如下面的例子是抓取 .App
,是虛擬 DOM
就會報錯
class TestPortal extends React.Component {
render () {
return createPortal (
<div>
test portal
</div>,
document.querySelector('.App')
);
}
}
常見的 Portal 方式就是製作 pop up。首先我們先製作一個共用的 PopUp
component,當它顯示的時候要能覆蓋整個視窗畫面,背景色為半透明,中間有一張白色卡片當作顯示文字的地方,並且只要點擊背景的部分就能關閉 pop up,PopUp
元件的位置是綁在 body
上面,程式碼如下
// PopUp.jsx
import React, { Component } from 'react';
import { createPortal } from 'react-dom';
import styles from './popUp.module.scss'
class PopUp extends React.Component {
render () {
const { closePopUp } = this.props
return createPortal (
<div className={ styles.popUp }>
<div className={ styles.card }>
<span>Pop up card</span>
<button onClick={closePopUp}>Close pop up</button>
</div>
</div>,
document.body
);
}
}
export default PopUp
//popUp.module.scss
.popUp {
position: absolute;
top: 0;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(10, 10, 10, 0.8);
.card {
width: 30%;
height: 30%;
background-color: white;
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
span {
text-align: center;
width: 100%;
}
}
}
接著我們在父元件中設定是否顯示 PopUp
元件的屬性 showPopUp
,並透過兩個函式 openPopUp()
、closePopUp()
來做開啟和關閉的功能,closePopUp()
必須傳給 PopUp
元件,這樣子就能夠利用父元件切換 PopUp
元件。
class UserProfile extends React.Component {
state = {
showPopUp: false
}
openPopUp () {
this.setState({ showPopUp: true })
}
closePopUp () {
this.setState({ showPopUp: false })
}
render() {
const { showPopUp } = this.state
return (
<div>
test component
<button onClick={ this.openPopUp.bind(this) }>open pop up</button>
{showPopUp &&
<PopUp closePopUp={ this.closePopUp.bind(this) } />
}
</div>
);
}
}
下圖分別是 pop up 展開前和展開後的畫面
React 中有兩個方式可以阻止冒泡事件,分別是 e.stopPropagation()
及 e.nativeEvent.stopImmediatePropagation()
,使用方式也很簡單,我們可以利用 onClick
在不希望被冒泡影響的元素上加上這兩個函式
return createPortal (
<div className={ styles.popUp } onClick={closePopUp}>
<div
className={ styles.card }
onClick={ e => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}}>
<span>Pop up card</span>
</div>
</div>,
document.body
);
元件內部使用 prop-types 也很簡易,只要使用 static
來調用即可
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class UserProfile extends React.Component {
static propTypes = {
name: PropTypes.string
}
render() {
const { userName } = this.props
return (
<div>
{ userName }
</div>
);
}
}
在專案開發中通常都會有許多父元件傳送參數到子元件的情境,如果能夠做好檢查 props
的行為,相對能夠省去不少開發和除錯時間。prop-types
是 React 提供的一個 props
屬性檢查套件,有兩種用法,一種是在元件內部,一種是在元件外部。
元件外部使用的方式很簡單,如下面的範例,我們從 prop-types
引用 PropTypes
,在 UserProfile
上設置 prototype
屬性,在裡面透過 PropTypes.string
去檢查 userName
是否為字串,userName
就是我們從外部傳進來的 props
屬性之一。
class UserProfile extends React.Component {
render() {
const { userName } = this.props
return (
<div>
{ userName }
</div>
);
}
}
UserProfile.propTypes = {
userName: PropTypes.string
}
元件內部使用 prop-types 也很簡易,只要使用 static
來調用即可
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class UserProfile extends React.Component {
static propTypes = {
name: PropTypes.string
}
render() {
const { userName } = this.props
return (
<div>
{ userName }
</div>
);
}
}
這一篇中除了 portal 的使用,還整理了 prop-types 的用法,透過這兩個功能,我們在開發上能夠更有效的運用元件並減少開發上的錯誤。其實個人覺得 prop-types 應該放在前面 props 章節一起整理才對,但因為這只是我照著課程一邊上網查資料所作的筆記,所以順序上是依照課程在跑,整理到了這邊確實也越來越多會運用到 props 的機會,因此還是選擇在這一篇才提到 prop-types。