副作用
副作用是當虛擬 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>
第一次渲染時記錄。