從零開始的 React 教學 Part 4 - Functional Components

Last updated on  Oct 2, 2022  in  ReactJS 前端框架  by  Amo Chen  ‐ 2 min read

先前的文章中都是以 class 形式製作 React 組件(component),但對於一些不需要狀態(state)的組建而言,使用 class 製作 React 組件不免有點多餘。所幸對於這種單純的組件,可以使用 function 進行開發。

本文環境

  • Chrome 86
  • React 16.13.1
  • nodejs 14.19.1
  • npm 8.7.0
  • webpack 3.3.8

Functional Components

使用 function 開發組件,大致類似以下形式的程式碼:

function 組件名(props) {
    return (    // 等同於 class 組件中的 render()
    )
}

因此 從零開始的 React 教學 Part 3 中的 BottomLine 可以改成以下:

import React from 'react';

function BottomLine(props) {
  return (
    <div style={{borderStyle: "solid", borderWidth: "0 0 1px 0"}}>
      {props.children}
    </div>
  )
}

export default BottomLine;

改成 functional component 後,比 class 組件更加簡潔!

當然, functional component 也能夠擁有 state ,不過需要使用 useState() 達成,例如以下範例的 Switch 組件,使用 const [value, setValue] = useState(false) 將 value 的預設值設定為 false, 並取得 1 個函式 setValue 用以後續更新 value 的值,該函式相當於 class component 中的 setState 。因此當我們觸發 onClick 時,透過呼叫 setValue 就能夠變更 value 的值,進而讓組件改變顯示的字串。

p.s. setValue 可以換成任何想要的名稱

import React, { useState } from 'react';

function Switch(props) {
  const [value, setValue] = useState(false)

  return (
    <div onClick={() => setValue(!value)}>
      switch: {value?'on':'off'}
    </div>
  )
}

上述範例可以放進 從零開始的 React 教學 Part 3 的 app.jsx 中,就能夠試玩,例如:

import React, { useState } from 'react';
import ReactDOM from 'react-dom';

import BottomLine from './textDeco';

function Switch(props) {
  const [value, setValue] = useState(false)

  return (
    <div onClick={() => setValue(!value)}>
      switch: {value?'on':'off'}
    </div>
  )
}

class HelloWorld extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
        count: 0,
      }
  }

  addCount = () => {
      let count = this.state.count + 1
      this.setState({count: count})
  }

  render() {
    return (
      <BottomLine>
        <Switch />
        <h1 onClick={this.addCount}>Hello World, {this.props.name} {this.state.count}
        </h1>
        <Switch />
      </BottomLine>
    );
  }
}

const domContainer = document.querySelector('#app');
ReactDOM.render(
  <HelloWorld name='React' />,
  domContainer
);

useEffect

除了 state 之外, functional component 也能夠使用 useEffect() 在組件渲染完成之後做些額外的事情,例如變更網頁標題:

function Character(props) {
  const value = 'Luke'

  useEffect(() => {
    console.log('after render')
    document.title = 'You are looking at ' + value
  })

  console.log('before render')
  return <div>{value}</div>
}

上述範例執行後,可以在開發者工具的 console 看到類似以下的輸出,足見 useEffect 確實會在組件渲染完成後才執行:

before render
after render

此外, useEffect 每次都會在渲染完成後執行。

The function passed to useEffect will run after the render is committed to the screen.

所以在 useEffect 中又讓組件重新渲染的話,譬如在 useEffect 更新 state 的值,將會再次觸發 useEffect , 然後又重新更新了 state 的值,導致組件再次重新渲染,進而使得組建沒有停止新渲染的一天,例如以下範例:

function Character(props) {
  const [value, setValue] = useState(0)

  useEffect(() => {
    console.log('after render');
    setValue(value + 1)
  })

  console.log('before render');
  return <div>{value}</div>
}

上述範例除 value 會無限遞增之外,也會在 console 中看到類似以下的錯誤訊息,該訊息也提及此錯誤可能由於我們在 useEffect 改變 state 所引起(state 的改變會造成 React 將組件重新渲染):

react_devtools_backend.js:2430 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
    in Character (created by HelloWorld)
    in div (created by BottomLine)
    in BottomLine (created by HelloWorld)
    in HelloWorld

此種不斷重新渲染的情況,將會造成效能問題,必須注意。

以上就是如何使用 React functional component 的介紹,如果有興趣的話,可以詳閱 Using the Effect Hook 一文,裡面還有很多本文未提及的部分喔!

Happy coding!

References

https://reactjs.org/docs/components-and-props.html

https://reactjs.org/docs/state-and-lifecycle.html

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!