React Component 管理 State 實作 Switch Button
React Component 介紹
React Component 主要可以分為兩種:Class-based Component、Functional Component。
Class-based Component
基於 ECMAScript 6 (ES6) 所引入的類別 (Class) 來創建 Component。Component 內部支援 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 支援的功能,例如:useState、useEffect 等等,來達成像 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 | 不支援 | 不限制,可以是 Number、String、Boolean、Object |
使用時機 | 需要使用 Lifecycle Hook | 單純 render JSX | 彙整相同程式邏輯供復用 |
參考資料