說明
支持我們

副作用

副作用是當虛擬 DOM 樹發生變更時執行的程式碼片段。它們不遵循接受 props 並傳回新虛擬 DOM 樹的標準方法,而且常常會超出樹的範圍來變異狀態或呼叫命令式程式碼,例如呼叫 DOM API。副作用也常被用來觸發資料擷取。

效果:函式元件中的副作用

我們在前面一個章節中學習 refs 和 useRef() 鉤子時,已經看過一個副作用的範例。一旦我們的 ref 被填入指向 DOM 元素的 current 屬性,我們需要一個方法來「觸發」與該元素互動的程式碼。

為了在渲染後觸發程式碼,我們使用了 useEffect() 鉤子,這是從函式元件建立副作用最常見的方法

import { useRef, useEffect } from 'preact/hooks';

export default function App() {
  const input = useRef()

  // the callback here will run after <App> is rendered:
  useEffect(() => {
    // access the associated DOM element:
    input.current.focus()
  }, [])

  return <input ref={input} />
}

請注意傳遞給 useEffect() 做為第二個引數的空陣列。當「依存項」陣列中的任何值從一次渲染變更到下一次渲染時,效果回呼會執行。例如,元件第一次渲染時,所有效果回呼都會執行,因為沒有先前的「依存項」陣列值可以比較。

我們可以將值新增到「依存項」陣列中,以根據條件觸發效果回呼,而不仅仅是在元件第一次渲染時。這通常用於在資料變更時執行程式碼,或當元件從頁面中移除(「卸載」)時執行程式碼。

我們來看一個範例

import { useEffect, useState } from 'preact/hooks';

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('<App> was just rendered for the first time')
  }, [])

  useEffect(() => {
    console.log('count value was changed to: ', count)
  }, [count])
  //  ^ run this any time `count` changes, and on the first render

  return <button onClick={() => setCount(count+1)}>{count}</button>
}

生命週期方法:類別元件副作用

類別元件也可以定義副作用,方法是實作 Preact 提供的任何可用 生命週期方法。以下是幾個最常用的生命週期方法

生命週期方法執行時機
componentWillMount在元件第一次渲染之前
componentDidMount在元件第一次渲染之後
componentWillReceiveProps在元件重新渲染之前
componentDidUpdate在元件重新渲染之後

在類別元件中使用副作用最常見的範例之一,是在元件第一次渲染時擷取資料,然後將資料儲存在狀態中。以下範例顯示一個元件,在第一次渲染後從 JSON API 要求使用者資訊,然後顯示該資訊。

import { Component } from 'preact';

export default class App extends Component {
  // this gets called after the component is first rendered:
  componentDidMount() {
    // get JSON user info, store in `state.user`:
    fetch('/api/user')
      .then(response => response.json())
      .then(user => {
        this.setState({ user })
      })
  }

  render(props, state) {
    const { user } = state;

    // if we haven't received data yet, show a loading indicator:
    if (!user) return <div>Loading...</div>

    // we have data! show the username we got back from the API:
    return (
      <div>
        <h2>Hello, {user.username}!</h2>
      </div>
    )
  }
}

試試看!

我們將讓這個練習保持簡單:變更右側的程式碼範例,讓它在每次 count 變更時記錄,而不再僅在 <App> 第一次渲染時記錄。

載入中...