React Hooks(基本のHook)

## 基本のフック
React組み込みのフックのうち、基本のフックを試していきます。

- useState
- useEffect
- useContext

[React Hooks(追加のHook)](https://qiita.com/takeshisakuma/items/5ea78670b6f5f3a779c5)


## useState

トグル、値の管理に使えます。
useStateを使わずに変数を定義した場合、コンポーネントの再描画時にリセットされてしまいます。
useState によって React の state の機能を関数コンポーネントに追加します。



```jsx
 const [count, setCount] = useState(intialState)
```







### トグル

```:src/Toggle.js
//関数コンポーネント内で 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
``` 
 
```:src/App.js 
import React from 'react'
import Toggle from './Toggle';
 
//コンポーネントApp
const App =() => {
 
  return(
 
    <>
      <Counter/>
    </>
 
  )
 
}
 
export default App
``` 





### 値を管理する

#### 数値をユーザー操作で増減させる

```:src/Cunter.js
// 関数コンポーネント内で 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
``` 

```:src/App.js 
import React from 'react'
import Counter from './Counter';
 
//コンポーネントApp
const App =() => {
 
  return(
 
    <>
      <Counter/>
    </>
 
  )
 
}
 
export default App
``` 



### 複数の値を1つのState変数で管理する

大人と子供の数を1つのstate変数(オブジェクト)でカウントします。

1つのState変数で複数の値を管理する場合、一部の値更新しても全体が上書きされてしまいます。
(新しい値を与えていない項目は値が消えてしまいます。)

そのため、スプレッド構文で一度state変数をすべて展開して、その上で更新したい値を上書きして、すべての項目を更新することが必要です。

```:src/Counter.js
// 関数コンポーネント内で 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
``` 
 
 
```:src/App.js 
import React from 'react'
import Counter from './Counter';
 
//コンポーネントApp
const App =() => {
 
  return(
 
    <>
      <Counter/>
    </>
 
  )
 
}
 
export default App
``` 
 



## useEffect

関数をレンダリング終了後に実行します。
副作用の処理(DOMの書き換え、変数代入、API通信など)に使います。



```:jsx
 useEffect(() => {
   console.log("レンダリングされた");
 })
```

### レンダリング終了ごとに決まった処理を行う

```:src/EffectSample.js
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
``` 

```:src/App.js 
import React from 'react'
import EffectSample from './EffectSample';
 
//コンポーネントApp
const App =() => {
 
  return(
 
    <>
      <EffectSample/>
    </>
 
  )
 
}
 
export default App
``` 




### 特定の値が変わったときだけ適用させる
useEffectの第2引数に変数を依存配列として渡すことで、変数を監視して変更があったときに適用させます。


カウントを2種類に増やし、カウントBが変化したときのみuseEffectを作動させてみます。
useEffectの第2引数に、監視する変数をセットします。

```:src/EffectSample.js
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
``` 
 

```:src/App.js 
import React from 'react'
import EffectSample from './EffectSample';
 
//コンポーネントApp
const App =() => {
 
  return(
 
    <>
      <EffectSample/>
    </>
 
  )
 
}
 
export default App
``` 








### 初回レンダリング時のみ実行する
useEffectの第2引数に空の配列を渡すと、初回レンダー時だけに発動させられます。




### クリーンアップ

#### クリーンアップを必要としない副作用の例
コードが実行されたあとすぐにそのことを忘れても構わないもの

- ネットワークリクエストの送信
- 手動での DOM 改変
- ログの記録



#### クリーンアップを必要とする副作用の例
メモリリークが発生しないようにクリーンアップが必要な処理
(何らかの外部のデータソースへの購読をセットアップするもの)

- イベントリスナー
- タイマー
- 非同期でデータを取得し画面に表示する
 
など


#### クリーンアップの記述方法

- cleanup() 関数
- アロー関数

```:jsx
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を共有する一番外側で値を渡します。

```:jsx
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を使った値の引き渡し
親→曾孫に受渡しできます。


親要素

```:src/App.js
// 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
``` 
 

子要素

```:src/Child.js
import React from 'react'
import GrandChild from './GrandChild'
 
 const Child = () => {
 
 return(
   <GrandChild/>
 )

}
 
export default Child
```



孫要素

```:src/GrandChild.js
import React from 'react'
import GreatGrandChild from './GreatGrandChild'
 
const GrandChild = () =>{
 
  return(
    <GreatGrandChild/>
  )
}

export default GrandChild
``` 


曾孫要素

```:src/GreatGrandChild.js
//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を使って渡していきます(バケツリレー)。

親要素

```:src\App.js
// 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
```
 

子要素

```:src\Child.js
import React from 'react'
import GrandChild from './GrandChild'

const Child = (props) => {
 
  return(
    <GrandChild data={props.data} text={props.text}/>
  )
 
}
 
export default Child
``` 

孫要素

```:src\GrandChild.js
import React from 'react'
import GreatGrandChild from './GreatGrandChild'
 
const GrandChild = (props) => {
 
  return(
    <GreatGrandChild data={props.data} text={props.text}/>
  )
 
}
 
export default GrandChild
``` 

曾孫要素

```:src\GreatGrandChild.js
import React from 'react'
 
const GreatGrandChild = (props) => {
 
  return (
    <p>{props.data.name}{props.data.age}歳「{props.text}」</p>
 
  )
 
}
 
export default GreatGrandChild
```