Hooks at a Glance(日本語訳)
【所要時間】
約2時間30分(2019年2月8日)
【概要】
- React公式ドキュメントのHooks at a Glanceの日本語訳。
- 1, Introducing Hooks(日本語訳)の続き。
- Hooksの機能について簡単に紹介。
【要約・学んだこと】
Hooksは後方互換性( backwards-compatible)がある。このぺージでは経験のあるReactユーザーに向けてHooksの概要を提供する。これは概要だ。混乱したら下記のような引用譜部分を参照する。
Detailed Explanation
なぜReactにHooksを導入するのかを学びたければMotivationを参照。
State Hook
この例ではcounterをレンダリングする。ボタンをクリックするとvalueが増える。
import React, { eState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ここでは、useStateがHook(これについてはすぐに説明する)だ。ローカルstateを追加するためにfunction componentの中でuseStateを呼び出す。Reactはリレンダリングの間このstateを維持する。
useStateはペアである:現在のstate valueとそれをアップデートするfunctionをreturnする。
このfunctionはイベントハンドラまたは、ほかの場所からcall出来る。これは古いstateと新しいstateをmergeしないという点で、classのthis.setStateに似ている(useStateとthis.stateの比較はUsing the State Hook参照)。
useStateの唯一の引数はinitial stateだ。上記の例ではカウンターがゼロから始まるので0となる。this.stateとの違いに注目すると、ここでのstateはobjectでなくてもよい。- 希望するならobjectでもいいが、initial state引数は最初のレンダリングの間のみで使われる。
Declaring multiple state variables
1つのcomponentに複数のState Hookを使うことも可能。
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
array destructuring構文を使うことで、useStateコールで宣言したstate variablesに異なる名前を付けることが出来る。これらの名前はuseState APIの機能ではない。代わりに、ReactはuseStateを繰り返し呼び出すなら、全てのレンダリング中に同じ順序で実行することを想定している。後ほどなぜこれが機能し、いつ役に立つのか説明する。
But what is a Hook?
Hooksはfunction componentからReact stateとライフサイクル機能を”hook into”することを可能にするfunctionだ。Hooksはclass内では動作しない。- Hooksはclassを使わずにReactを使うことを可能とする(既存のcomponentを夜な夜な書き直すことは推奨しないが、新しいcomponentでHooksを使い始めることが出来る)。
ReactはuseStateのようなbuilt-in Hooksをいくつか提供している。また、異なるcomponent間でstatefulな挙動を再利用するためのオリジナルのHookを作ることも可能。まずはbuilt-inのHooksを見ていく。
Detailed Explanation
State Hookの詳細は、専用ページを参照: Using the State Hook
Effect Hook {#️-effect-hook}
dataの取得、サブスクリプション、React componentから手動でDOMの変更などをこれまでに実行したことがあるでだろう。これらをside effects(副作用)”(または省略して”effects”)と呼ぶ。なぜなら、それらは他のcomponentに影響を与え、レンダリングの間に終えることが出来ないからだ。
Effect Hook, useEffectはfunction componentからside effectを実行する能力を追加する。それはReactのclassのcomponentDidMount, componentDidUpdate, componentWillUnmountと同じ目的を果たすが、単一のAPIに統合された。(Using the Effect HookでuseEffectとこれらのメソッドを比較した例を見せる)
例えば、このcomponentはReactがDOMをアップデートした後にdocument titleをセットする。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useEffectをcallすると、ReactにDOMの変更がフラッシュした後に”effect” functionを実行させる。Effectsはcomponent内部で宣言されるので、propsとstateにアクセスできる。デフォルトでは、Reactはレンダリングごとにeffectsを実行する。- これは最初のレンダリングを含む。(Using the Effect Hookでこれとclassライフサイクルがどのように比較されるかを説明する。)
Effectsはオプションでfunctionをreturnすることで、それらの後にどうやって”clean up”するのかを指定することもできる。例えば、このcomponentはfriend’s online statusをsubscribeするためにeffectを使い、unsubscribingすることでclean upする。
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
この例では、Reactはcomponentがunmoutされたとき、またその後のレンダリングによるeffectを再実行する前に、ChatAPIからunsubscribeする(ChatApiに渡したprops.friend.idが変わらないなら、Reactにre-subscribingするのをスキップさせる方法( tell React to skip re-subscribing)もある。)。
useStateと同様に、1つのcomponent内で複数使用することも可能:
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
// ...
Hooksは、ライフサイクルメソッドに基づいて分割を強制するのではなく、どの部分が関係するかによってcomponentのside effectを整理することが出来る。
Detailed Explanation
useEffectの詳細については Using the Effect Hookを参照。
Rules of Hooks
HooksはJavaScript functionだが、2つのルールを課す:
- Hooksはtop levelでのみcallする。Hooksはloop, condition, nested functionではcallしない。
- HooksはReact function componentでのみcallする。通常のJavaScript functionからはcallしない(他に1つだけHooksがコールできる場所がある。- オリジナルのHooksだ。これについては後ほど学ぶ)。
これらのルールを自動で施行する linter pluginを提供している。これらのルールは限定的で、最少は混乱するのものだと理解しているが、Hooksがきちんと動作するためには重要な要素となる。
Detailed Explanation
これらのルールについては Rules of Hooksを参照。
Building Your Own Hooks
我々はstateful logicをcomponent間で再利用したいこともある。従来、この問題を解決するのに2つの方法がよく使われる。: higher-order components と render propsだ。カスタムHooksは、treeにcomponentを追加することなくこれらを可能にする。
先ほどこのページで、useStateとuseEffect Hooksをcallして、friend’s online statusをsubscribeするFriendStatus componentを紹介した。このsubscription logicを他のcomponentで再利用したいとしてみよう。
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
これは引数としてfriendIDを持ち、friendがonlineかどうかをreturnする。
これを両方のcomponentで使うことが出来る。
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
これらのcomponentのstateは完全に独立している。Hooksはstateful logicを再利用する方法のことで、state自体を再利用するのではない。実際に、Hookを呼ぶ毎に、完全に分離されたstateを持つ。- そのため、1つのcomponent内で同じカスタムHookを使うことさえ出来る。
form, handling, animation, declarative subscriptions, timers, その他考えたこともない幅広いユースケースをカバーするcustom Hooksを書くことが出来る。我々はReactコミュニティからどんな面白いcustom Hookが登場するか楽しみにしている。
Detailed Explanation
custom Hooksの詳細については Building Your Own Hooksを参照。
Other Hooks
あまり一般的に使われないが、役に立つであろうbuilt-in Hooksがいくつかある。例えば、 useContext
はネスティングを導入せずにReact contextをsubscribe出来る。
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
そして useReducer
はreducerで複雑なcomponentのstateを管理することを可能とする。
function Todos() {
const [todos, dispatch] = useReducer(todosReducer);
// ...
Detailed Explanation
built-in Hooksについては Hooks API Reference参照。
Next Steps
もし何か理解できないことや詳細を学びたいことがあれば、次のページの State Hookに進もう。
また、 Hooks API referenceやHooks FAQを見てもよい。
最後に、introduction pageのなぜHooksを追加したか、どうやってアプリのリライトをせずにclassと並行して利用を始めるかも見逃さないように。
【わからなかったこと】
特になし。
【感想】
やっとHooksが登場。”Hooksはstateful logicを再利用する方法のことで、state自体を再利用するのではない。”がメリットと思われます。