基本のフック
React組み込みのフックのうち、基本のフックを試していきます。
- useState
- useEffect
- useContext
useState
トグル、値の管理に使えます。
useStateを使わずに変数を定義した場合、コンポーネントの再描画時にリセットされてしまいます。
useState によって React の state の機能を関数コンポーネントに追加します。
const [count, setCount] = useState(intialState)
トグル
//関数コンポーネント内で state を使えるようにするため、useState をインポート
import React, { useState } from 'react'
//関数コンポーネントCounterを定義
const Toggle = () => {
// conditionという名前のstate変数を宣言、初期値 true をセット
const [condition, setCondition] = useState(true)
// 関数toggleを作成(state変数のconditionがtrueだとtrue、falseだとfalseを返す)
const toggle = () => setCondition(!condition)
return (
<>
{/* ボタンを押すと関数toggleを実行する*/}
<button onClick={toggle}>{condition ? 'アクティブ' : '非アクティブ'}</button>
</>
)
}
export default Toggle
import React from 'react'
import Toggle from './Toggle';
//コンポーネントApp
const App =() => {
return(
<>
<Counter/>
</>
)
}
export default App
値を管理する
数値をユーザー操作で増減させる
// 関数コンポーネント内で state を使えるようにするため、useState をインポート
import React, { useState } from 'react'
//関数コンポーネントCounterを定義
const Counter = () => {
//定数initialState初期値として使う0を指定
const intialState = 0
//countという名前のstate変数を作成し、初期値にintialStateをセット(countを更新するときはsetCountを使う)
const [count, setCount] = useState(intialState)
return (
<>
{/* 現在値を表示 */}
<p>現在の数字は{count}です</p>
{/* ボタンを押すとsetCount関数でcountを更新 setCount(現在値=>現在値+変更させる値)の形にする */}
<button onClick={() => setCount(prevCount => prevCount + 1)}>
+ 1
</button>
{/* ボタンを押すとsetCount関数でcountを更新 setCount(現在値=>現在値+変更させる値)の形にする */}
<button onClick={() => setCount(prevCount => prevCount - 1)}>
- 1
</button>
{/* 定数initialStateを使って、countを初期値に戻す */}
<button onClick={() => setCount(intialState)}>初期値に戻す</button>
</>
)
}
export default Counter
import React from 'react'
import Counter from './Counter';
//コンポーネントApp
const App =() => {
return(
<>
<Counter/>
</>
)
}
export default App
複数の値を1つのState変数で管理する
大人と子供の数を1つのstate変数(オブジェクト)でカウントします。
1つのState変数で複数の値を管理する場合、一部の値更新しても全体が上書きされてしまいます。
(新しい値を与えていない項目は値が消えてしまいます。)
そのため、スプレッド構文で一度state変数をすべて展開して、その上で更新したい値を上書きして、すべての項目を更新することが必要です。
// 関数コンポーネント内で state を使えるようにするため、useState をインポート
import React, { useState } from 'react'
//関数コンポーネントCounterを定義
const Counter = () => {
//定数initialState初期値として使う0を指定
const intialState = {
adult:0,
child:100
}
//countという名前のstate変数を作成し、初期値にintialStateをセット(countを更新するときはsetCountを使う)
const [count, setCount] = useState(intialState)
return (
<>
{/* 現在値の表示を表示 */}
<p>大人は{count.adult}人です</p>
<p>子供は{count.child}人です</p>
{/* ボタンを押すとsetCount関数でcountを更新 setCount(現在値=>現在値+変更させる値)の形にする */}
<button onClick={() => setCount({...count,adult:count.adult+1})}>
大人 + 1
</button>
{/* ボタンを押すとsetCount関数でcountを更新 setCount(現在値=>現在値+変更させる値)の形にする */}
<button onClick={() => setCount({...count,adult:count.adult-1})}>
大人 - 1
</button>
{/* ボタンを押すとsetCount関数でcountを更新 setCount(現在値=>現在値+変更させる値)の形にする */}
<button onClick={() => setCount({...count,child:count.child+1})}>
子供 + 1
</button>
{/* ボタンを押すとsetCount関数でcountを更新 setCount(現在値=>現在値+変更させる値)の形にする */}
<button onClick={() => setCount({...count,child:(count.child-1)})}>
子供 - 1
</button>
{/* 定数initialStateを使って、countを初期値に戻す */}
<button onClick={() => setCount(intialState)}>初期値に戻す</button>
</>
)
}
export default Counter
import React from 'react'
import Counter from './Counter';
//コンポーネントApp
const App =() => {
return(
<>
<Counter/>
</>
)
}
export default App
useEffect
関数をレンダリング終了後に実行します。
副作用の処理(DOMの書き換え、変数代入、API通信など)に使います。
useEffect(() => {
console.log("レンダリングされた");
})
レンダリング終了ごとに決まった処理を行う
import React, {useState, useEffect} from 'react'
const EffectSample = () => {
//state変数cuntの作成(初期値0)
const [count, setCount] = useState(0)
//レンダリング後に実行する処理
useEffect(() => {
console.log("レンダリングされた");
})
return (
<>
<p>{`${count}回クリックされた`}</p>
<button onClick={()=>{
setCount(prevCount => prevCount + 1)
console.log("+1された");
}}>
+1
</button>
<button onClick={()=>{
setCount(0);
console.log("resetされた");
}}>
リセット
</button>
</>
)
}
export default EffectSample
import React from 'react'
import EffectSample from './EffectSample';
//コンポーネントApp
const App =() => {
return(
<>
<EffectSample/>
</>
)
}
export default App
特定の値が変わったときだけ適用させる
useEffectの第2引数に変数を依存配列として渡すことで、変数を監視して変更があったときに適用させます。
カウントを2種類に増やし、カウントBが変化したときのみuseEffectを作動させてみます。
useEffectの第2引数に、監視する変数をセットします。
import React, {useState, useEffect} from 'react'
const EffectSample = () => {
//state変数ocuntの作成(初期値0)
const [countA, setCountA] = useState(0)
const [countB, setCountB] = useState(0)
//レンダリング後に実行する処理(countBが変動する結果のレンダリングに限定)
useEffect(() => {
console.log("カウントBが変化した");
},[countB])
return (
<>
<p>{`カウントAが${countA}回クリックされた`}</p>
<div>
<button onClick={()=>{
setCountA(prevCount => prevCount + 1)
console.log("カウントAが+1された");
}}>
+1
</button>
<button onClick={()=>{
setCountA(0);
console.log("カウントAがresetされた");
}}>
リセット
</button>
</div>
<p>{`カウントBが${countB}回クリックされた`}</p>
<div>
<button onClick={()=>{
setCountB(prevCount => prevCount + 1)
console.log("カウントBが+1された");
}}>
+1
</button>
<button onClick={()=>{
setCountB(0);
console.log("カウントBがresetされた");
}}>
リセット
</button>
</div>
</>
)
}
export default EffectSample
import React from 'react'
import EffectSample from './EffectSample';
//コンポーネントApp
const App =() => {
return(
<>
<EffectSample/>
</>
)
}
export default App
初回レンダリング時のみ実行する
useEffectの第2引数に空の配列を渡すと、初回レンダー時だけに発動させられます。
クリーンアップ
クリーンアップを必要としない副作用の例
コードが実行されたあとすぐにそのことを忘れても構わないもの
- ネットワークリクエストの送信
- 手動での DOM 改変
- ログの記録
クリーンアップを必要とする副作用の例
メモリリークが発生しないようにクリーンアップが必要な処理
(何らかの外部のデータソースへの購読をセットアップするもの)
- イベントリスナー
- タイマー
- 非同期でデータを取得し画面に表示する
など
クリーンアップの記述方法
- cleanup() 関数
- アロー関数
useEffect(() => {
elm.addEventListener('click', () => {})
// コンポーネントがアンマウントされるときに呼ばれる(アロー関数で書く場合)
return () => {
elm.addEventListener('click', () => {})
}
// コンポーネントがアンマウントされるときに呼ばれる(cleanup()関数で書く場合)
//return function cleanup() {
// elm.addEventListener('click', () => {}
//}
}, [])
useContext
Context
コンポーネントからグローバルにアクセスできるデータです。
コンポーネントツリーのデータの橋渡し
Context/Props | 説明 |
---|---|
Context | 下の階層で Contextに収容されているデータにアクセスできる(すべての階層ごとに渡す必要性がなくなる) |
Props | バケツリレー |
useContext
親からPropsで渡されなくても、Contextに入れたデータにアクセスできます。
説明 | |
---|---|
createContext | コンテキスト作成 |
useContext | コンテキストを呼び出す |
Contextを共有する一番外側で値を渡します。
import React, {useContext} from 'react'
//context作成してexport
export const DataContext = createContext();
const Parent =() => {
//contextで共有するデータを作成
const data = {
key1: '値1',
key2: '値2'
};
return(
{/* 共有するデータを渡す */}
<DaraContext.Provider value={data}>
<ChildComponent/>
</DataContext.Provider>
)
}
Contextを使った値の引き渡し
親→曾孫に受渡しできます。
親要素
// ReactからcreateContextとuseStateをimport
import React, {createContext,useState} from 'react'
//ContextA.jsのContextコンポーネントをインポート
import Child from './Child'
//createContextでContextを作成(DataContextとTextContext)してexport
export const DataContext = createContext();
export const TextContext = createContext();
function App() {
//dataを作成
const [data,setData] = useState({
name: '山田',
age: '20'
});
//textを作成
const [text,setText] = useState('こんにちは');
return (
<div className='App'>
{/*DataContext.Providerを作成、valueには「data」をセット*/}
<DataContext.Provider value={[data,setData]}>
{/*TextContext.Providerを作成、valueには「text」をセット*/}
<TextContext.Provider value={[text,setText]}>
<Child/>
</TextContext.Provider>
</DataContext.Provider>
</div>
)
}
export default App
子要素
import React from 'react'
import GrandChild from './GrandChild'
const Child = () => {
return(
<GrandChild/>
)
}
export default Child
孫要素
import React from 'react'
import GreatGrandChild from './GreatGrandChild'
const GrandChild = () =>{
return(
<GreatGrandChild/>
)
}
export default GrandChild
曾孫要素
//useContextをインポート
import React, {useContext} from 'react'
//AppコンポーネントからDataContext, TextContextをimport
import {DataContext, TextContext} from './App'
const GreatGrandChild = () => {
//変数dataにDataContextを代入
const [data,setData] = useContext(DataContext)
//変数textにTextContextを代入
const [text,setText] = useContext(TextContext)
return (
<p>{data.name}{data.age}歳「{text}」</p>
)
}
export default GreatGrandChild
Propsを使った値の引き渡し
親→子→孫→曾孫の順にPropsを使って渡していきます(バケツリレー)。
親要素
// ReactからcreateContextとuseStateをimport
import React, {useState} from 'react'
//ContextA.jsのContextコンポーネントをインポート
import Child from './Child'
function App() {
//dataを作成
const [data,setData] = useState({
name: '山田',
age: '20'
});
//textを作成
const [text,setText] = useState('こんにちは');
return (
<div className='App'>
<Child data={data} text={text}/>
</div>
)
}
export default App
子要素
import React from 'react'
import GrandChild from './GrandChild'
const Child = (props) => {
return(
<GrandChild data={props.data} text={props.text}/>
)
}
export default Child
孫要素
import React from 'react'
import GreatGrandChild from './GreatGrandChild'
const GrandChild = (props) => {
return(
<GreatGrandChild data={props.data} text={props.text}/>
)
}
export default GrandChild
曾孫要素
import React from 'react'
const GreatGrandChild = (props) => {
return (
<p>{props.data.name}{props.data.age}歳「{props.text}」</p>
)
}
export default GreatGrandChild