DXエージェンシースパイスファクトリー株式会社、エンジニアのワンです。
スパイスファクトリーのはこちら。
この記事では、React 初心者の方に向けて、react hooks についての基本説明と、いくつかの hook の紹介をしていきます。
Contents
hookとは
そもそも、 hook とは何でしょうか。
React 公式サイトでは、hooks について以下のように説明してあります。
フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります。
要は、hook は Reactの state やライフサイクルの機能などを、関数コンポーネント内に使用できるようにするための関数です。
hookを使用する理由
React 公式ドキュメントでは、クラスコンポーネントより hook を用いる関数コンポーネントの方がおすすめだとされています。
なぜクラスコンポーネントではなく関数コンポーネントを使用した方が良いのでしょうか。
実際に両者のソースコードを比較していきましょう。
クラスを使った場合
クラスは this を使用しています。 例えば this.setState などの特殊な状態処理メソッドを指します。
しかし、this は JavaScript の特別なキーワードであり、状況によって異なるものを参照するため、分かり辛い場合があります。
- メソッドの中では、クラスオブジェクトを指す
- イベントハンドラーでは、イベントを受け取った要素を指す
それに加えて、コードが多くなると更に複雑になります。
以下はクラスコンポーネントの簡単な例です。
import React from 'react';
class HelloComponent extends React.Component {
constructor(props) {
super(props);
this.state = { name: '' };
this.handleChange = this.handleChange.bind(this);
};
handleChange(event) {
this.setState({ name: event.target.value });
};
render() {
const { name } = this.state
return (
<div>
<input value={name} onChange={this.handleChange} />
<p>Hello {name}</p>
</div>
);
};
};
hook を使って関数コンポーネントとして定義した場合
次のコードは、上と同じコンポーネントを関数コンポーネントとして書いたものです。
import React, { useState } from 'react';
const HelloComponent: React.FC = () => {
const [name, setName] = useState('');
const handleChange = (event) => {
setName(event.target.value)
};
return (
<div>
<input value={name} onChange={handleChange} />
<p>Hello {name}</p>
</div>
);
};
こちらは this を使用せず、 useState という hook を使って name の値を管理しています。
クラスコンポーネントと比べてコードの量が少なく、読みやすい印象です。
hook でロジックをカプセル化できるので、UIとロジックの分離が容易になります。
また、一度定義した hook は、関数コンポーネント内と他の hook 内でも使いまわせます。
把握しておくべきhookのルール
hook には2つのルールがあります。
hook の使用にあたって、まずこれらを理解しておく必要があります。
- 関数コンポーネントでのみ使用でき、クラスコンポーネントでは使用できない
- hookの呼び出しの順番は、毎回のレンダリングで同じである必要がある
尚、呼び出しの順番に関しては、if 条件やループ、ネストした関数の中に hook を入れることもできませんのでその点もご注意ください。
Reactの公式hook紹介
続いて、React が公式で出している hook について紹介します。
ここでは最もよく使われるという理由から useState と useEffect をピックアップしてご説明します。
useState と useEffect 以外にも、useContext、useRef、useCallback などの hook があるので、詳しく知りたい方はこちらの記事を参照してください。
https://ja.reactjs.org/docs/react-api.html#hooks
useState
import { useState } from 'react';
const [state, setState] = useState(initialState);
useState は state の値とその値を更新するためにセッター関数 setState を返します。
ステート管理のため、クラスコンポーネントのthis.stateとthis.setState()の代わりに使えます。
useEffect
import { useEffect } from 'react';
useEffect(() => {
// 実行処理
}, []));
useEffect は、クラスコンポーネントの componentDidMount、componentDidUpdate、componentWillUnmount のメソッドの代わりに使えます。
1つ目の引数として定義した関数の実行が2つ目の引数の値によって変わります。イメージとしては以下です。
- 2つ目の引数の配列が指定されてない場合は、何かのステートが変わる度に実行されます。
- 2つ目の引数の配列が空だったら、componentDidMount と同じ動きになります。
- 2つ目の引数の配列に変数を指定すると、その変数に変化があったときに実行されます。
その他おすすめhook
その他にも便利な hook はたくさんあります。
react hook form
react hook form はフォームを検証するのに役立つライブラリです。
hook の形にしたメソッドで使用されます。
以下は react-hook-form を使った簡単な例です。
import { useForm } from 'react-hook-form';
const HelloFormComponent = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => console.log('Hello: ' + data.name);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input defaultValue="" {...register("name")} placeholder="名前" />
{errors.exampleRequired && <span>必須項目です</span>}
<input type="submit" />
</form>
);
};
custom hooks
また、独自の hook を定義することもできます。
こちらは custom hook が集まっているレポジトリです。
https://github.com/streamich/react-use
過去作成したhook紹介
これまで私自身が作成した hook についても1つピックアップしてご紹介します。
useIsScrollAtEnd
この hook は isScrollAtTop と isScrollAtBottom を返します。scroll の位置が一番上もしくは一番下にあるかどうかを教えてくれます。
import { useCallback, useEffect, useState } from 'react';
import throttle from 'lodash/throttle';
const fps = 12;
const useIsScrollAtTopBottom = (
targetDom: HTMLDivElement | (Window & typeof globalThis) = window,
scrollTopThreshold = 0
) => {
const [isScrollAtTop, setIsScrollAtTop] = useState(false);
const [isScrollAtBottom, setIsScrollAtBottom] = useState(false);
const target = targetDom;
const handleScroll = useCallback(() => {
const scrollPosition =
'pageYOffset' in target ? target.pageYOffset : target.scrollTop;
const scrollHeight =
'document' in target
? target.document.documentElement.scrollHeight
: target.scrollHeight;
const containerHeight =
'innerHeight' in target ? target.innerHeight : target.clientHeight;
const maxScrollTop = scrollHeight - containerHeight;
if (scrollPosition = maxScrollTop) {
setIsScrollAtBottom(true);
} else {
setIsScrollAtTop(false);
setIsScrollAtBottom(false);
}
}, [target, scrollTopThreshold]);
const throttledScroll = throttle(handleScroll, 1000 / fps);
useEffect(() => {
targetDom.addEventListener('scroll', throttledScroll);
return () => targetDom.removeEventListener('scroll', throttledScroll);
}, [handleScroll]);
return { isScrollAtTop, isScrollAtBottom };
};
以下は実際の使い方です。
isScrollAtBottom の値が true になったら、loadNextPage() で次のページを取得する処理を実行します。
そうすると、無限スクロールを実現することができます。
const { isScrollAtBottom } = useIsScrollAtTopBottom();
useEffect(() => {
if (isScrollAtBottom) {
loadNextPage();
}
}, [isScrollAtBottom]);
useIsScrollAtEnd のフックを実装することで、無限スクロールを簡単に実現できるようになりました。
hookは、シンプルで利便性の高い機能
関数コンポーネント + hook はクラスコンポーネントの代わりに使用できます。
クラスよりもシンプルで、コードがとても読みやすくなります。
また、hook は関数コンポーネント内のどこでも再利用できるので、そういった観点からもとても便利です。
是非、皆さまも活用してみてください。
また、弊社では開発からサービスの運用まで、幅広くご支援させていただいております。
是非一度ください。
また、詳しいサービス内容に関しては、当社ホームページのをご参照ください。
About The Author
w_tu