Hooksは、classを記述せずにstateやその他のReactの機能を使えるようにしたもので、今は提案段階です。RFCはこちら
現在はReact v16.7.0-alphaで試せます。
Hooks導入の動機はこちら
https://qiita.com/tatane616/items/9d667e32b6f82f9f7a9f
State Hook
よくあるボタンを押すとカウントが増えていくサンプルです。
import { useState } 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とstateを更新する関数の2つを返します。
古いstateと新しいstateを一緒にマージしないと言う点をのぞいて、classのthis.state
に似ています。
useState
の引数の0
は初期値で、this.state
とは違いオブジェクトである必要はありません。
State Hookは1つのコンポーネントで複数利用でき、その場合は次のように記述します。
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
Hooksは、stateやライフサイクルメソッドを関数コンポーネントから利用する機能で、class内では機能しません。
Effect Hook
useEffect
は関数コンポーネントで副作用を実装できるようにしたHooksで、データをフェッチしてきたり、DOMをいじったりなど今までclass内のcomponentDidMount
,componentDidUpdate
,componentWillUnmount
でやってきたロジックを単一のAPIに統合すると言うもの。
DOMのアップデート後にtitleを変えるサンプル
import { 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>
);
}
Effectsはコンポーネント内で宣言され、propsやstateにアクセスが可能。デフォルトでは、初回を含むレンダリングのたびに実行されます。
Effectsでは、任意で関数を返すことによってどのようにクリーンアップするかを指定することもできます。
次のコードは、友人のオンラインステータスをsubscribeし、のちにunsubscribeするサンプルです。
import { 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';
}
この例では、コンポーネントがアンマウントされる時や再レンダリングでEffectが実行される時にChatAPI
をunsubscribeします。
useState
と同様に、Effectも1つのコンポーネントに複数宣言することができます。
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のルール
HooksはJSの関数ですが、2つのルールがあります。
- トップレベルでのみ呼び出す。ループやifなどの条件文、ネストされた関数の中で呼び出してはいけない。
- Reactの関数コンポーネントでのみ呼び出す。普通のJSの関数で呼び出してはいけない。
Custom Hooks
ステートを持ったロジックをコンポーネント間で再利用したい時は、今まではHOCやrender propsで解決していたと思いますが、Custom Hooksを使うことでツリーに余計なコンポーネントを追加せずに実装することができます。
先ほどのオンラインステータスのコードを例に見ていきましょう。
これらのロジックをuseFriendStatus
と言うHookに抽出します。
import { 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
を引数にとって、友人がオンライン状態かどうかを返します。
このCustom Hooksは以下のように利用することができます。
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>
);
}
上記のコンポーネントのstateはそれぞれ独立しています。Hooksはステートフルなロジックを再利用するもので、それ自体がstateを持つわけではありません。
関数の名前をuse
から始めることで、それを他のコンポーネントから呼んだ際にCustom Hookと言うことができます。
useSomething
と言う名前は、リンタープラグインなどでHooksを利用したコードでのバグを見つけることなどに役立ちます。
Other Hooks
他にもuseContext
やuseReducer
といった便利なHooksがあります。
useContext
は、ネストすることなくReact Contextを利用することができます。
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
useReducer
は複雑なコンポーネントでのローカルステートをreducerを使って管理できるようになります。
function Todos() {
const [todos, dispatch] = useReducer(todosReducer);
// ...