副作用
副作用とは、仮想 DOM ツリー内で変更が発生したときに実行されるコードの一部です。これらは、props
を受け取って新しい仮想 DOM ツリーを返すという標準的なアプローチに従わず、多くの場合、ツリーから離れて状態を変化させたり、DOM API の呼び出しなどの命令型コードを呼び出したりします。副作用は、データフェッチをトリガーする方法としてもよく使用されます。
エフェクト: 関数コンポーネントにおける副作用
以前の章で、refと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()
への2番目の引数として渡される空の配列に注目してください。エフェクトコールバックは、その「依存関係」配列の値がレンダリング間で変更された場合に実行されます。たとえば、コンポーネントが最初にレンダリングされるとき、比較する以前の「依存関係」配列の値がないため、すべてのエフェクトコールバックが実行されます。
コンポーネントが最初にレンダリングされたときだけでなく、条件に基づいてエフェクトコールバックをトリガーするために、「依存関係」配列に値を追加できます。これは通常、データ変更に応答してコードを実行したり、コンポーネントがページから削除された(「アンマウントされた」)ときに実行するために使用されます。
例を見てみましょう。
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 | コンポーネントが再レンダリングされた後 |
クラスコンポーネントでの副作用の最も一般的な使用例の1つは、コンポーネントが最初にレンダリングされたときにデータをフェッチし、そのデータを状態に格納することです。次の例は、最初にレンダリングされた後に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>
)
}
}
試してみよう!
この演習は簡単にしておきましょう。<App>
が最初にレンダリングされたときだけでなく、count
が変更されるたびにログに記録するように、右側のコードサンプルを変更してください。