React React Component React State React Hooks Switch Button

React Component 管理 State 實作 Switch Button

陳柏睿 2019/11/05 14:18:25
102

React Component 介紹

React Component 主要可以分為兩種:Class-based ComponentFunctional Component

 

Class-based Component

基於 ECMAScript 6  (ES6) 所引入的類別 (Class) 來創建 ComponentComponent 內部支援 State 以及 Lifecycle Hook通常用來處理 Component 內部細膩的控制與複雜的程式邏輯。

創建 Class-based Component 範例:

class SampleComponent extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}

透過以上範例可以看到,創建 Class-based Component 必需繼承 React.Component;render JSX 必須使用 render() 這個 Lifecycle Hook 來 return JSX;使用 this.props 取得從外部傳進 Component 的值 ( this 指向 Component 本身 )。

 

Functional Component

以函數的定義方式來創建 Component,單純是一個普通的 JavaScript 函數。 React 還沒推出 Hooks 之前 ( React 16.8.0 更早的版本 ),Functional Component 不支援類似 State 以及 Lifecycle Hook 相關的功能,通常單純用來 render React Element (JSX);React 16.8.0 版本推出 Hooks 後,使 Functional Component 可以使用 Hooks 支援的功能,例如:useStateuseEffect 等等,來達成像 Class Component State 以及 Lifecycle Hook 的功能。

創建 Functional Component 範例:

const SampleComponent = (props) => {
  return <h1>Hello, {props.name}</h1>
}

透過以上範例可以看到,創建 Functional Component 相較簡單一些,單純使用 JavaScript function 創建 ( 以上範例使用 ES6 arrow function ); render JSX 則是直接 return JSX;透過 props 參數來取得從外部傳進 Component 的值。

以上兩個範例的輸出結果會是相等的。

 

 

 

管理 State

React Component 內部的 State 是用來決定 Component 的渲染和行為方式,可以透過 State 的設置與操作,使 Component 具有動態和交互性。

 

Class-based Component 管理 State

首先需要先初始化 state,在 Component 內設置一個名為 state key-value Object接著針對需求設定自己需要的 state若要更新 state,則使用 setState()。更新 state React 會重新 render component

設置 state 範例:

class SampleComponent extends React.Component {
  state = {
    count: 0,
    status: true
  };
}

修改 state 範例:

handleIncrement = () => {
  this.setState({ count: this.state.count + 1 });
}

 

Hooks ( Functional Component ) 管理 State

在使用 Functional Component 時,是否導入 Hooks 完全是自己可以選擇的,但是若要在 Functional Component 中管理 State,則必須導入 Hooks 才能達成目的。

注意:若要使用 Hooks,務必將所有 React Package Upgrade 16.8.0 版本或是更高的版本。

使用 Hooks 有兩個規則必須遵循:

1. 只在最上層呼叫 Hook。

範例:

const SampleComponent = () => {
  const [ counter, setCounter ] = useState(0);
}

嚴禁寫成:

const SampleComponent = () => {
  if (status) {
    const [counter, setCounter] = useState(0)
  }
}

( 以上是錯誤的範例 )

說明:不要在迴圈、條件式或是巢狀的 Function 內呼叫 Hooks。必須在 React Function 的最上層調用 Hooks藉由遵循這些規則,才能確保 React 在每次 render component 時,Hooks 都會按照正確的順序被呼叫,使得 React 有辦法在多個 useState 呼叫時,正確的保持 Hooks State

2. 只在 React Function 呼叫 Hooks。

(1) React Function Component 中呼叫 Hooks

(2) 在自定義的 Hooks 中呼叫 Hooks

說明:透過遵循以上規則,確保在 Component 中所有的 Stateful 邏輯在原始碼中可以清楚的被看見。

設置 state 範例:

const SampleComponent = () => {
  // Hooks 支援宣告多個 State
  const [ loginStatus, setLoginStatus ] = useState(false);
  const [ counter, setCounter ] = useState(0);
  const [ user, setUser ] = useState({
    account: 'boris.chen',
    name: 'Boris Chen',
    age: 27,
    vip: true
  });
}

修改 state 範例:

const handleIncrement = () => {
  setCounter(counter + 1);
}

 

 

 

實作 Switch Button

Switch Button 在許多網站、APP 都隨處可見,甚至一些 UI Library 都有提供 Switch button 可以套用。但有時候還是會想自己寫看看,此篇文章在這裡展示如何分別使用 Class-based Component Hooks (Functional Component) 搭配 State 來實作 Switch Button

 

建立 Switch Button Component

首先,先建立 Switch Button Component。將 Switch Button 獨立出來,好處是可以復用。

Switch Button Component 程式碼如下: ( SwitchButton.js )

import React from 'react';
import styled from 'styled-components';

// Styled Components
const StyledSwitchButton = styled.div`
  display: flex;
  flex-direction: row;
  align-items: center;
  button {
    width: 38px;
    background-color: ${props => props.active ? '#22E222' : '#FFFFFF'};
    border-radius: 11px;
    border: 1px solid ${props => props.active ? '#22E222' : '#ECECEC'};
    box-sizing: border-box;
    padding: 0;
    transition: all 300ms ease-in-out;
    cursor: pointer;
    outline: none;
    &::after {
      content: '';
      width: 20px;
      height: 20px;
      background-color: #FFFFFF;
      border-radius: 50%;
      box-shadow: 0px 1px 3px rgba(30, 30, 30, 0.3);
      transition: all 300ms ease-in-out;
      transform: ${props => props.active ? 'translate(16px)' : 'translate(0)'};
      display: block;
    }
  }
  span {
    color: #989898;
    font-size: 12px;
    margin-left: 10px;
  }
`;

// Switch Button Component
// 透過 props 接收外部傳進來三個屬性的值
// 1. type: 設置 Switch Button 按鈕類型。
// 2. active: Switch Button 狀態。
// 3. clicked: toggleSwitchButton (Method) 切換 Switch Button。
const SwitchButton = (props) => (
  <StyledSwitchButton active={props.active}>
    <button type={props.type} onClick={props.clicked}></button>
    <span>{props.active ? 'Enable' : 'Disabled'}</span>
  </StyledSwitchButton>
);

export default SwitchButton;

說明:由於 Switch Button 只需要透過 props 接受外部傳進來得值以及顯示按鈕狀態及樣式,並沒有複雜的邏輯需要處理,因此在這裡使用 Functional Component 的定義方式來呈現 Switch Button。

 

Class-based Component 搭配 State 實作 Switch Button

Class-based Component 程式碼如下: ( ClassBasedComponent.js )

import React, { Component, Fragment } from 'react';

// import Components
import SwitchButton from '../SwitchButton/SwitchButton';

// Class-based Component
class ClassBasedComponent extends Component {
  // 使用 state (object) 管理 state
  state = {
    isEnable: true
  };

  // 切換 Switch Button (Method)
  toggleSwitchButton = () => {
    // 修改 state
    this.setState({ isEnable: !this.state.isEnable });
  }

  // 傳三個屬性給 Switch Button
  // 1. type: 設置 Switch Button 按鈕類型。
  // 2. active: Switch Button 狀態。
  // 3. clicked: 傳 toggleSwitchButton (Method) 給 Switch Button 觸發 click 事件切換 Switch Button。
  render() {
    return (
      <Fragment>
        <h3>Class-based Component</h3>
        <SwitchButton type="button" active={this.state.isEnable} clicked={this.toggleSwitchButton} />
      </Fragment>
    );
  }
}

export default ClassBasedComponent;

 

Hooks (Functional Component) 搭配 State 實作 Switch Button

Hooks (Functional Component) 程式碼如下: ( FunctionalHooksComponent.js )

import React, { Fragment, useState } from 'react';

// import Components
import SwitchButton from '../SwitchButton/SwitchButton';

// Functional Component
const FunctionalHooksComponent = () => {
  // 使用 useState 管理 state
  const [ state, setState ] = useState({
    isEnable: false
  });

  // 切換 Switch Button (Method)
  const toggleSwitchButton = () => {
    // 修改 state
    setState({ isEnable: !state.isEnable });
  }

  // 傳三個屬性給 Switch Button
  // 1. type: 設置 Switch Button 按鈕類型。
  // 2. active: Switch Button 狀態。
  // 3. clicked: 傳 toggleSwitchButton (Method) 給 Switch Button 觸發 click 事件切換 Switch Button。
  return (
    <Fragment>
      <h3>Functional Component</h3>
      <SwitchButton type="button" active={state.isEnable} clicked={toggleSwitchButton}>Functional</SwitchButton>
    </Fragment>
  );
}

export default FunctionalHooksComponent;

 

完成 Switch Button 實作

完整程式碼如下:

 

 

 

Class-based & Functional & Hooks 三者比較

  Class-based Component Functional Component Hooks
宣告方式 ES6 Class JavaScript function JavaScript function
State 數量限制 單個 不支援 單個 or 多個
State 型別限制 必須是一個 Object 不支援 不限制,可以是 NumberStringBooleanObject
使用時機 需要使用 Lifecycle Hook 單純 render JSX 彙整相同程式邏輯供復用

 

 

 

參考資料

React 官方文件 - React Component
React 官方文件 - React Hooks

陳柏睿