ReactにおけるGlobal State管理

#HooksとContext APIでGlobal stateを管理する

Reacttでの状態管理といえばReduxで、Global stateの管理のために長年一緒に使われてきました。 Hooksが実装されてからは、小規模なアプリなどの開発においてはReduxを使わずにHooksとContext APIを使ってGlobal stateを管理する方法がより望ましいのではと思うので、そちらについて書いていきます。

##Context ProviderとData Store
![スクリーンショット 2022-02-19 13.16.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/770055/03442ea7-bb08-03f7-462b-72d3b27c8fa6.png)

####1.Data Store(contextAPI(createContext))の作成

```jsx
// src/contexts/AppContext.js

import { createContext } from 'react'

const AppContext = createContext()

export default AppContext
```




####2.Context ProviderでComponentを囲う&DataStoreの作成(initial state, reducer)
データを提供する側(provider)=親
使いたい側(consumer)＝子  という認識。
Context.Providerで囲まれた中にあるconsumerは、providerのデータを使うことができるようになるという仕組み。

＊更新処理関数のreducerの処理は別ファイルで記載のため、割愛。

```jsx
// src/components/App.js

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

import EventForm from './EventForm'
import Events from './Events'
import OperationLogs from './OperationLogs'
import AppContext from '../contexts/AppContext.js'
import reducer from '../reducers'

const App = () => {   
  const initialState = {  //初期値を設定
    events: [],
    operationLogs: []
  }
  const [state, dispatch] = useReducer(reducer, initialState)


  return (
    <AppContext.Provider value={{ state, dispatch }}>
        <EventForm />
        <Events />
        <OperationLogs />
    </AppContext.Provider>
  )
}

export default App
```

####3.stateを参照したいcomponent内でuseContextを使う
関数の引数にstateを渡すのではなく、useContextを使って渡してあげることで、そのcomponentに必要な状態やオブジェクトだけを渡すことができるようになる。

```jsx
// src/components/Events.js

import React, { useContext } from 'react'

import Event from './Event'
import AppContext from '../contexts/AppContext.js'

const Events = () => {  //引数ではなるべく値を渡さない
  const { state } = useContext(AppContext)
   
  return (
    <>
      <h4>イベント一覧</h4>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>タイトル</th>
            <th>ボディー</th>
            <th></th>
          </tr>
        </thead>
        <tbody>     
          { state.events.map((event, index) => (<Event key={index} event={event} />))}　
        </tbody>
      </table>
    </>
  )
}   //dispatchで渡ってくるデータをmapで一覧表示。mapにはkeyが必須(そうすることでuniqueなidが振られたデータを一つずつ表示する)
//propを使ってeventは渡す
//indexのところはもちろんevent.idでもいいが、標準の機能に則ってindexとした方がバグが起きない

export default Events
```

####孫コンポーネントでもstateやdispatchが使えるようになる
useContextによって、コンポーネントがどれだけ複雑な階層を持っていたとしても、どこでも状態やオブジェクトを参照できるようになる。（これが最大のメリットかと。）



```jsx
// src/components/Event.js

import React, { useContext } from 'react'

import {
  ADD_OPERATION_LOG,
  DELETE_EVENT
} from '../actions'
import AppContext from '../contexts/AppContext.js'
import { timeCurrentIso8601 } from '../utils'

const Event = ({ event }) => {
  const { dispatch } = useContext(AppContext) 
  const id = event.id   
  const handleClickDeleteButton = () => {
    const result = window.confirm(`イベント(id=${id})を本当に削除しても良いですか？`)
    if (result) {
      dispatch({ type: DELETE_EVENT, id })    //Storeの更新にはdispatchを使用

      dispatch({
        type: ADD_OPERATION_LOG,
        description: `イベント(id=${id})を削除しました。`,
        operatedAt: timeCurrentIso8601()
      })
    }
  }

  return (
    <tr>
      <td>{id}</td>
      <td>{event.title}</td>
      <td>{event.body}</td>
      <td><button type="button" onClick={handleClickDeleteButton}>削除</button></td>
    </tr>
  )
}

export default Event
```

参考：
https://eight-bites.blog/2021/11/global-state-with-hooks/
https://www.udemy.com/course/youtube-react-hooks-css-module/learn/lecture/19962412#overview