Edited at

【React】ざっくり入門(Hooks)


概要

React16.8から新しい機能としてHooksが導入されました。

以下の順序でHooksとは何かざっくり入門していきましょう。


Hooksとは?

Hooksとは従来classコンポーネントでしか使用できなかったstate等の機能をfunctionalコンポーネント(関数コンポーネント)でも使用可能にする機能です。

この記事では以下の3種類のhookを紹介します。


  • (1)State Hook:statesetStateを関数コンポーネント内で使用可能にする。

  • (2)Effect Hook:データ取得やDOM変更といったside effectsを関数コンポーネント内で使用可能にする。

  • (3)Custom Hooks:共通のロジックをコンポーネント間での再利用を可能にする。


Hooksの規則

Hooksを使用する際には以下の規則を守りましょう。


  • (1) Hooksは必ずコンポーネント中の一番上(トップレベル)に定義する。

  • ループ、条件分岐、ネストされた関数の中で定義してはいけません。)

  • (2) HooksはReactの関数コンポーネントの中だけで呼ぶ。(通常のJavascriptの関数から使用してはいけません。)


State Hook

useStateと呼ばれるhookを使用する事でstatesetStateを関数コンポーネント内で行える様にします。

classコンポーネントuseStateを使用した関数コンポーネントを比較して使用方法を確認して見ましょう。


従来のclassコンポーネント

以下の例では主に2つの事を行なっています。


  • (1)stateで初期値に0を持つcountを定義する。

  • (2)setStateでボタンを押すたびにcount1を足している。

output2

class Example extends React.Component {

constructor(props) {
super(props);
this.state = {
count: 0
};
}

render() {
return (
<div>
<p>{this.state.count} 回クリックしました。</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
クリックしてください。
</button>
</div>
);
}
}


StateHook

上記と全く同じ事をuseStateを使用して行います。

基本的な使用方法(定義方法)は以下の通りです。

const [state, setState] = useState(initialState);

useStateでは従来のstatesetStateを同時に設定しする様なイメージです。

上記のcountやそのsetStateuseStateを使用して定義すると以下の様になります。

import React, { useState } from  'react';

function Counter() {
const [count, setCount] = useState(0)
}

以下の3点に注目して上のコードを見てみましょう。


  • (1) countがclassコンポーネントでいうthis.state={count:0}の部分。

  • (2) setCountがclassコンポーネントでいうsetStateの部分。

  • (3) useState()に渡されている0countの初期値。

また以下の2点を踏まえて残りのコードを完成させます。


  • (1) stateを直接参照できる。

  • (2) setCountを使用してstateを更新できる。

import React, { useState } from  'react';

function Counter() {
const [count, setCount] = useState(0)

return(
<div>
// (1) stateを直接参照できる。
<p>{count} 回クリックしました</p>

// (2) setCountを使用してstateを更新できる。
<button onClick={() => setCount(count + 1)}> クリックしてください。</button>
</div>
)
}

export default Counter;

ちなみに以下の様にして複数のstateを定義していきます。

import React, { useState } from  'react';

function Counter() {
const [count, setCount] = useState(0)
const [name, setName] = useState('')

return(
<div>
<p>{count} 回クリックしました</p>
<button onClick={() => setCount(count + 1)}> クリックしてください。</button>
<p>私の名前は{name}</p>
<button onClick={() => setName('テスト太郎')}>名前表示</button>
</div>
)
}

export default Counter;


Effect Hook

従来のclassコンポーネントではcomponentDidMountcomponentDidUpdatecomponentWillUnmount等の中でデータ取得やDOM変更を行なっていましたが、これをuseEffectと呼ばれるhookを使用し、関数コンポーネントでも使用可能にします。

こちらもuseEffectの使用方法を従来のclassコンポーネントと比較して確認していきます。


従来のclassコンポーネント

従来はcomponentDidMountはマウントされた直後に1回だけ呼ばれ、componentDidUpdateはコンポーネントが更新された後に呼ばれ、ここでデータの取得等を行いました。

output3

import React from  'react';

class Effect extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}

componentDidMount() {
console.log('マウントされました。')
}

componentDidUpdate() {
console.log('アップデートされました。')
}

render() {
return (
<div>
<h1>{this.state.count} 回クリックしました。</h1>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
クリックしてください。
</button>
</div>
);
}
}


EffectHook

useEffectは従来のcomponentDidMountcomponentDidUpdatecomponentWillUnmountの3つを組み合わせた様なもので、基本的にrenderされる毎に呼ばれます。(first renderを含む。)

hooksを使用した開発ではここでside effectsを実行します。

output4

import React, { useState, useEffect } from 'react'

function Effect() {
const [count, setCount] = useState(0)

useEffect(() => {
console.log('レンダーされました。')
})

return(
<div>
<h1>{count} 回クリックしました</h1>
<button onClick={() => setCount(count + 1)}> クリックしてください。</button>
</div>
);
}


Custom Hooks

上記で記載した通り、共通のロジックをコンポーネント間での再利用を可能にします。

以下の2つのコンポーネントがあるとします。


  • (1) 受け取ったid1だったらログイン中ですを返す、status.js

  • (2) 受け取ったid1だったらおかえりなさいを返すmessage.js


status.js

export default function Status(props){

const [isLoggedIn, setIsLoggedIn] = useState(false);

const handleStateChange = (id) => {
if(id === 1){
setIsLoggedIn(true)
}
else{
setIsLoggedIn(false)
}
}

useEffect(() => {
handleStateChange(props.user.id)
})

const status = isLoggedIn ? 'ログイン中' : 'サインアップ'

return (
<>
<h1>Status: {status}</h1>
</>
)
}



message.js

export default function Message(props){

const [isLoggedIn, setIsLoggedIn] = useState(false);

const handleStateChange = (id) => {
if(id === 1){
setIsLoggedIn(true)
}
else{
setIsLoggedIn(false)
}
}

useEffect(() => {
handleStateChange(props.user.id)
})

const message = isLoggedIn ? 'おかえりなさい' : 'あなたは誰ですか?'

return (
<>
<h1>Message: {message}</h1>
</>
)
}


ここの共通したロジック部分をcustom Hookを使用する事で、以下の様に抜き出す事ができます。

抜き出したロジックのコンポーネントはuseから始まる慣習があります。今回はuseLoginと名付けました。


useLogin

import { useState, useEffect } from 'react';

export default function useLogin(userId){
const [isLoggedIn, setIsLoggedIn] = useState(false);

// コンポーネント間で再利用したいロジック
const handleStateChange = (id) => {
if(id === 1){
setIsLoggedIn(true)
}
else{
setIsLoggedIn(false)
}
}

// side effectsを実行する。
useEffect(() => {
handleStateChange(userId)
})

return isLoggedIn;
}


useLoginを使用する事でstatus.jsmessage.jsをよりスッキリ書くことができます。


user.js

import React from 'react';

import useLogin from './useLogin';

export default function Status(props){
const status = useLogin(props.user.id) ? 'ログイン中' : 'サインアップ'
return (
<>
<h1>Status: {status}</h1>
</>
)
}



message.js

import React from 'react';

import useLogin from './useLogin';

export default function Message(props){
const message = useLogin(props.user.id) ? 'おかえりなさい' : 'サインアップしてください'
return (
<>
<h1>Message: {message}</h1>
</>
)
}


custom hooksでは他にも色々な事ができるので、詳細は公式ドキュメントで確認ください。


参照

hooksの説明としては本当にざっくりでしたので、詳細は公式ドキュメントで確認ください。