Hooks at a Glance(日本語訳)

Tatsuya Asami
14 min readFeb 8, 2019

--

【所要時間】

約2時間30分(2019年2月8日)

【概要】

【要約・学んだこと】

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 componentsrender 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 referenceHooks FAQを見てもよい。

最後に、introduction pageのなぜHooksを追加したか、どうやってアプリのリライトをせずにclassと並行して利用を始めるかも見逃さないように。

useState

【わからなかったこと】

特になし。

【感想】

やっとHooksが登場。”Hooksはstateful logicを再利用する方法のことで、state自体を再利用するのではない。”がメリットと思われます。

--

--

Tatsuya Asami
Tatsuya Asami

Written by Tatsuya Asami

Front end engineer. React, TypeScript, Three.js

No responses yet