iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
0
Modern Web

React + GraphQL 全端練習筆記系列 第 2

React 基礎簡介 - React Component 與 Hooks

本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。

React component 的主要目的就是回傳一個 React element,看是作為children傳遞給親代element,或是給ReactDOM.reneder掛到html。

製作component有兩個方法: Class component 與 Function component。

這邊會大略談談class 跟 function的用法與差異,不過因為專案主要使用function component + hooks,class component的部分就不會太深入。

Class component

定義一個繼承 React.Component 的class就做成了class component,簡單範例:

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

render這個function負責回傳element,是component中唯一必須被定義的,通常主要的JSX都寫在這。

當然只有render的話作為component不太有用,一般還要制定state,定義event handler,不過這些屬於component的共通觀念,待function compnent介紹完一併討論。

Function component

Function component相較class更簡單:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

只要定義一個回傳element的function就算。

State

那為什麼有兩種component呢?
兩者間最主要的差異在於class能夠保有自己的state,紀錄並更新部件的狀態,繼而更新畫面,而function原本純粹作為產生element的工具,不帶有自己的state。

看看帶有state的class component:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 為了讓 `this` 能在 callback 中被使用,這裡的綁定是必要的:
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

在constructor中用 this.state 宣告初始的state,就能在JSX代入並反映在畫面上。

而要更新state的話,用的是 this.setState()這個功能,像上面handleClick裡用的那樣。
React裡要更新state只能用這種方式,不能直接賦值給this.state,這麼做的話並不會觸發React更新畫面的機制。

另外class裡的function要調用this的話,都需要在constructor進行綁定,
像是這行:

this.handleClick = this.handleClick.bind(this);

如果沒麼做,就在function裡呼叫this.setState的話會報錯:

Component 生命週期

而除了state以外,class比function多了的功能還有生命週期相關的函式,像是componentDidMount(),componentWillUnmount()等,能在生成畫面的步驟間加入更多細節,詳細在這邊不做介紹,請參考官方文件

Hooks

雖然上面提到class跟function component間的功能差異,不過在Hooks的推出後這些差異被一口氣填平了許多,Hooks就是一系列能讓function component達成原先只有class component能做到的功能的函式,這裡挑幾個作簡介。

  1. useState
const [state, setState] = useState(initialState);

在function component作如上宣告的話就能在function component裡像class component一樣保存狀態並反映在畫面上,跟class component一樣的是要更新狀態的話必須經由setState,不能直接賦值。

state跟setState的名稱是可以改的,像是

const [count, setCount] = useState(initialCount);

所以多次呼叫useState設定不同的state是可以的,不像class component要把全部的state包在一起:

const [count, setCount] = useState(initialCount);
const [amount, setAmount] = useState(initialAmount);

使用上要注意的就是更新的時候,是會把整個狀態給覆蓋掉的,所以如果state是個包含多個key的object的話,當你想只更新部分key的話不能只代入那幾個key,這樣的話其他key會消失。

像是這樣:

const [state,setState] = useState({foo:"foo",bar:"bar"});

setState({bar:"foobar"}) //state會變成{bar:"foobar"},foo消失了

所以可利用ES6的展開語法:

setState(prevState => {
  // Object.assign would also work
  return {...prevState, bar:"foobar"};
});

prevState是更新前的state,用展開語法把整個前state代入後再用一樣的key把目標值覆蓋更新。

  1. useEffect

useEffect會在每一次render後被執行,它可以用來代替一些class component的生命週期功能,像是componentDidMount()。

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

useEffect有兩個參數,第一個就是render後被執行的function,而第二個則是可選的,以陣列代入的參數。

第二個參數用於管理useEffect呼叫的時機,主要有三個狀況:

  1. 省略
    省略第二個參數的話就會在每回的render後執行這個useEffect。

  2. 包含依賴值們的陣列
    所謂的"依賴值"指的時當這些值被更新的時候,才會在render後觸發useEffect,不然若在render時這些值跟前回一樣保持不變的話,這個useEffect就會被跳過。

  3. 空陣列
    如果陣列裡沒有任何依賴值,這個useEffect在第一次render後就只會執行一次,因為沒有可以觸發更新的值。

在有用依賴值管理useEffect觸發的時候,React不建議在useEffect呼叫外部的function,因為這樣只看useEffect不會知道該function用了哪些參數,該把那些參數設為依賴值,詳細說明見此

因為有了Hooks,使用function component變的便利許多,撰寫上也較class component輕便,接下來的實作用的都是function component 加 hooks。

References:


上一篇
React 基礎簡介 - React element與JSX
下一篇
React 基礎簡介 - Props,State與事件處理
系列文
React + GraphQL 全端練習筆記30

尚未有邦友留言

立即登入留言