iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
0
自我挑戰組

React 30 天學習歷程系列 第 20

【Day 20】React Portals 和 PropTypes 檢查

  • 分享至 

  • xImage
  •  

Portal 是 React 提供來讓子元件可以在父元件外渲染的一種方式,官網文件有提到一個經典案例,就是當父元件有著 overflow: hidden 或是 z-index 的 CSS 屬性,但我們需要讓子元件在視覺上能夠呈現在父元件外,就需要使用 Portal,如下圖,點擊父元件中的按鈕後彈出子元件,由於父元件有 overflow: hidden 屬性,子元件是不會顯示在父元件外,這時候就可以使用 Portal。

ReactDOM.createPortal(child, container)

Portal 是透過 ReactDOM.createPortal(child, container) 這個方法來實現,childcontainer 兩個參數分別有以下的用途

  • child 是我們要插入的 React DOM,它可以是 JSX 或是字串。
  • container 是我們要插入的地方,它必須是真實存在的 DOM
    Portal 的使用相當簡易,首先我們要先從 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

常見的 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>
        );
    }
}

PropTypes 檢查

在專案開發中通常都會有許多父元件傳送參數到子元件的情境,如果能夠做好檢查 props 的行為,相對能夠省去不少開發和除錯時間。prop-types 是 React 提供的一個 props 屬性檢查套件,有兩種用法,一種是在元件內部,一種是在元件外部。

元件外部使用 prop-types

元件外部使用的方式很簡單,如下面的範例,我們從 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

元件內部使用 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。


上一篇
【Day 19】 受控組件(Controlled Component)與非受控組件(Uncontrolled Component)
下一篇
【Day 21】React 中請求遠端資料
系列文
React 30 天學習歷程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言