Ver16.8(2019)から導入された新機能
Classコンポーネントを使わずに、Functionalコンポーネントを使います。
これによって、コーディングの簡素化、可読性の向上が実現されます。
useState
useStateのおかげで、状態の保持や変更が可能になりました。
Props Drilling問題の解決
a/b/cという階層になっていた場合、aからcに値を私たければ、
propsを経由してa→b→cの順序で辿る必要がありました。
Hooksでは、useContextを使ってa→cに直接値を渡すことが可能になりました。
Reduxの使用を最小限にできる
Hooksが持っている、useContext, useReducerを組み合わせることで、Reduxに近いことができるので「useContext, useReducerで解決できない複雑なことだけReduxを使う」という
ことができるようになりました。
Functional Components
stateもlifecycleも持たず、propsを受け取りElementを返すだけのコンポーネントです。
Props
渡す値に応じて結果が動的に変わるようにできる。
↓app.jsxからname="successful man"をbasic.jsxに渡し、それを利用します。
import Basic1 from "./components/Basic1";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Basic1 name="successful man" />
</header>
</div>
);
}
export default App;
import React from 'react'
const basic1 = (props) => {
return (
<>
<h1>You are {props.name} </h1>
</>
)
}
export default basic1
イベントハンドラー
buttonにonClickイベントを付与する。
import React from 'react'
const basic1 = (props) => {
const clickHandler = () => {
console.log("成功");
};
return (
<>
<button onClick={clickHandler}>Click</button>
<h1>You are {props.name} </h1>
</>
)
}
export default basic1
useState
↓オブジェクトに対して使う
↓プロダクトの名前と価格をフォームに入力すると表示されるプログラム
import React, {useState} from 'react';
const Basic1 = () => {
const [product, setProducts] = useState({name: "", price: ""})
return (
<>
<form>
<input type="text" value={product.name}
onChange={evt => setProducts({...product, name: evt.target.value})}/>
<input type="text" value={product.price}
onChange={evt => setProducts({...product, price: evt.target.value})}/>
</form>
<h1>Product name is {product.name} </h1>
<h1>Product price is {product.price} </h1>
</>
)
}
↓配列に対して使う
import React, { useState } from 'react'
const Basic2 = () => {
const [ products, setProducts ] = useState([])
const newProducts = () => {
setProducts([...products, {
id: products.length,
name: "Heloo React"
}])
}
return (
<div>
<button onClick={newProducts}>Add New Product</button>
<ul>
{products.map(product => (
<li key={product.id}>{product.name} id: {product.id} </li>
))}
</ul>
</div>
)
}
useEffect
useEffectはHooksの中でも複雑なフックのひとつです。
公式の説明では「副作用を実行するフック」となっています。
useEffectの使い方はuseEffect(func)です。このときfuncはすぐさま実行されず、
コンポーネントのレンダリング後に実行されます。つまり簡単にいうとuseEffectは
「関数の実行タイミングをReactのレンダリング後まで遅らせるフック」ということになります。
「コンポーネントが読み込まれた最初の1回だけ処理を実行したい」だったり、
「あるStateが変化したときだけある処理を実行したい」といったときに使われます。
import React, {useState, useEffect} from 'react'
const UseEffect = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("useEffect invoked")
}, [])
return (
<div>
<button onClick={() => setCount(prevCount=>prevCount+1)}>Click {count} </button>
</div>
)
}
export default UseEffect
useEffect(( ) => {
console.log("useEffect invoked")
}, [ ])
のように、useEffectの第2引数に[ ]を取ると、
最初のレンダリング時のみ第1引数の中が出力されるようになります。

↓useEffectの第2引数に[count]というState名を取ると、countにのみ反応するようになります。
↓反応して欲しいStateを第2引数に取るのです。
import React, {useState, useEffect} from 'react'
const UseEffect = () => {
const [count, setCount] = useState(0);
const [item, setItem] = useState("")
useEffect(() => {
console.log("useEffect invoked")
}, [count])
return (
<div>
<button onClick={() => setCount(prevCount=>prevCount+1)}>Click {count} </button>
<input type="text" value={item} onChange={evt=>setItem(evt.target.value)}/>
</div>
)
}
export default UseEffect
上と同コードで第2引数を空にすると、countだけでなくinputに応じても反応するようになってしまいます。

useEffect(Cleannup)
useEffectでは「Cleannup関数」を登録できます。
そもそもuseEffectはレンダリングのたびに呼ばれます。
なので副作用の中でイベントリスナーの登録やタイマーのセットをしていると、
何重にも副作用が走ることになります。そこで「Cleannup関数」をreturnすることで、
2度目以降のレンダリング時に前回の副作用を消してしまうことができます。
import React, {useState, useEffect} from 'react'
const Timer = () => {
const [count, setCount] = useState(0);
const time = () => {
setCount(prevCount => prevCount + 1);
}
useEffect(() => {
const interval = setInterval(time, 1000);
return () => {
clearInterval(interval);
console.log("cleared");
}
}, [])
return (
<div>
{count}
</div>
)
}
export default Timer
import React, {useState} from 'react'
import Timer from './Timer';
const TimerContainer = () => {
const [display, setDisplay] = useState(true);
return (
<div>
<button onClick={() => setDisplay(!display)}>Switch Timer</button>
{ display && <Timer /> }
</div>
)
}
export default TimerContainer
1秒ずつ増えていくタイマーで、Switch Timerを押すとカウントが非表示になり、
useEffectのreturnが返されます。

useContext
Props Drilling問題の解決
a/b/cという階層になっていた場合、aからcに値を私たければ、
propsを経由してa→b→cの順序で辿る必要がありました。
Hooksでは、useContextを使ってa→cに直接値を渡すことが可能になりました。
useContext + useReducer = Reduxに近い機能
C.jsxでApp.jsxの値を表示できています。
import React from 'react'
import C from './C'
const B = () => {
return (
<div>
<h3>B</h3>
<C />
</div>
)
}
export default B
import React, { useContext } from 'react'
import AppContext from '../contexts/AppContext'
const C = () => {
const value = useContext(AppContext)
return (
<div>
<h3>C</h3>
{ value }
</div>
)
}
export default C
import logo from './logo.svg';
import './App.css';
import B from './components/B';
function App() {
return (
<AppContext.Provider value={'value from App.js'}>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<B />
</header>
</div>
</AppContext.Provider>
);
}
export default App;
useReducer
コンポーネント内での状態管理をしてくれます。
newState = reducer(currentState, action)
reducer関数の引数に、現在のStateの値と、
現在のStateに対してどのようなアクションを与えたいかという情報を指定します。
reducerの内部で定義されたロジックに基づいて、新しいStateを返してくれます。
[state, dispatch] = useReducer(reducer, initialState)
useReducerの引数にreducerと一番最初のState(initialState)を渡してあげます。
すると、useReducerがstateという変数とdispatchという関数を作って返してくれます。
state = Function内でカレントstateを参照できる変数
dispatch = 指定されたactionに基づいてreducer関数を実行してくれる関数
状態遷移の管理
ケースに応じてstateをアップデートしてくれます。
import React, { useReducer } from 'react'
const initialState = 0;
const reducer = (currentState, action) => {
switch(action) {
case 'add_1':
return currentState + 1;
case 'multiple_3':
return currentState * 3;
case 'reset':
return initialState;
default:
return currentState;
}
}
const BasicReducer = () => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<div>Count {count}</div>
<button onClick={() => dispatch('add_1')}>Add + 1</button>
<button onClick={() => dispatch('multiple_3')}>Multiple * 3</button>
<button onClick={() => dispatch('reset')}> Reset</button>
</div>
)
}
export default BasicReducer
useReducer + useContext
useReducer = コンポーネント内での状態管理
useContext = 孫コンポーネントからのdispatch実行
useReducerとuseContextを組み合わせることで、
グローバルな状態遷移の管理を可能にしています。
孫コンポーネントのCで実行したdispatchに基づいて、
親コンポーネントが表示しているcountの値が変わります。
↓
import logo from './logo.svg';
import './App.css';
import AppContext from './contexts/AppContext';
import CompB from './components/CompB';
import { useReducer } from 'react'
const initialState = 0;
const reducer = (currentState, action) => {
switch(action) {
case 'add_1':
return currentState + 1;
case 'multiple_3':
return currentState * 3;
case 'reset':
return initialState;
default:
return currentState;
}
}
function App() {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{countProvided: count, dispatchProvided: dispatch}}>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
Count {count}
<CompB />
</header>
</div>
</AppContext.Provider>
);
}
export default App;
import React from 'react'
import CompC from './CompC'
const CompB = () => {
return (
<div>
<CompC />
</div>
)
}
export default CompB
import React, { useContext } from 'react'
import AppContext from '../contexts/AppContext'
const CompC = () => {
const {dispatchProvided} = useContext(AppContext);
return (
<div>
<button onClick={() => dispatchProvided('add_1')}>add + 1</button>
</div>
)
}
export default CompC
useMemo
同じ結果を返す処理に関しては初回のみ処理を実行しておき、
2回目以降は前回の処理結果を呼び出すことで毎回同じ処理を実行しなくてよくなります。
これはプログラミングではメモ化と呼ばれるテクニックで、
それをReact Hooks上で簡単に利用できるのがuseMemoとuseCallbackです。
functionalコンポーネントのトップレベルの位置で宣言を行いメモ化したい変数を定義します。
第2引数にはuseEfectと同様に依存変数を配列で指定しておくことで、
依存変数に変更があった場合には再処理が実行されるようになります。
useMemoの第2引数にcount1指定しているので
count1は再計算が行われ、count2では行われません。
なので、count2の方がクリック時の反応が速くなっています。
import React, { useState, useMemo } from 'react'
const Memo = () => {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const AddCount1 = () => {
setCount1(prevCount1 => prevCount1 + 1 )
}
const AddCount2 = () => {
setCount2(prevCount2 => prevCount2 + 1 )
}
const isOdd = useMemo(() => {
let i = 0;
while(i < 500000000)
i++
return count1 % 2 !== 0;
}, [count1])
return (
<div>
<button onClick={AddCount1}>Count1: {count1}</button>
<span>{isOdd ? 'Odd number' : 'Even number'}</span>
<button onClick={AddCount2}>Count2: {count2}</button>
</div>
)
}
export default Memo
useCallback
useCallbackはメモ化したコールバック関数を返すHooks APIです。
useCallbackを利用しない場合はコールバック関数はfunctionalコンポーネントの再描画のたびに
新しい関数インスタンスを生成してイベントとしてバインドされていきます。
useCallbackを利用することで関数をメモ化して新しい関数インスタンスを生成せずに再描画後のイベントとして再利用を行います。
↓count1を押したときはcount1、count2を押したときはcount2のみレンダリングされるようにし
↓たい。

import logo from './logo.svg';
import './App.css';
import AppContext from './contexts/AppContext';
import { useReducer, useState, useCallback } from 'react';
import CountDisplay from './components/CountDisplay';
import CountClick from './components/CountClick';
const initialState = 0;
const reducer = (currentState, action) => {
switch(action) {
case 'add_1':
return currentState + 1;
case 'multiple_3':
return currentState * 3;
case 'reset':
return initialState;
default:
return currentState;
}
}
function App() {
const [count, dispatch] = useReducer(reducer, initialState);
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const AddCount1 = () => {
setCount1(prevCount1 => prevCount1 + 1 )
}
const AddCount2 = () => {
setCount2(prevCount2 => prevCount2 + 1 )
}
return (
<AppContext.Provider value={{countProvided: count, dispatchProvided: dispatch}}>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<CountDisplay name="count1" count={count1} />
<CountClick handleClick={AddCount1}>AddCount1</CountClick>
<CountDisplay name="count2" count={count2} />
<CountClick handleClick={AddCount2}>AddCount2</CountClick>
</header>
</div>
</AppContext.Provider>
);
}
export default App;
↓
↓AddCount1,AddCount2の関数をuseCallbackで囲み、
↓useCallbackの第2引数に反応して欲しいpropsを指定すると、
count1を押したときはcount1、count2を押したときはcount2のみ
レンダリングされるようになりました。
import logo from './logo.svg';
import './App.css';
import AppContext from './contexts/AppContext';
import { useReducer, useState, useCallback } from 'react';
import CountDisplay from './components/CountDisplay';
import CountClick from './components/CountClick';
const initialState = 0;
const reducer = (currentState, action) => {
switch(action) {
case 'add_1':
return currentState + 1;
case 'multiple_3':
return currentState * 3;
case 'reset':
return initialState;
default:
return currentState;
}
}
function App() {
const [count, dispatch] = useReducer(reducer, initialState);
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const AddCount1 = useCallback(() => {
setCount1(prevCount1 => prevCount1 + 1 )
}, [count1])
const AddCount2 = useCallback(() => {
setCount2(prevCount2 => prevCount2 + 1 )
}, [count2])
return (
<AppContext.Provider value={{countProvided: count, dispatchProvided: dispatch}}>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<CountDisplay name="count1" count={count1} />
<CountClick handleClick={AddCount1}>AddCount1</CountClick>
<CountDisplay name="count2" count={count2} />
<CountClick handleClick={AddCount2}>AddCount2</CountClick>
</header>
</div>
</AppContext.Provider>
);
}
export default App;







