Gatsby+Firestoreでステートフックを利用したCRUD

# Gatsby+Firestoreでステートフックを利用したCRUD

参考チュートリアルはこちら[Build a CRUD Firestore App in React/Gatsby with Hooks](https://dev.to/vetswhocode/build-a-crud-firestore-app-in-react-gatsby-with-hooks-4ig9)

## 前提
- すでにnpmとGitをインストール済み。
- Firebase（https://firebase.google.com） にログインできる状態。
- ReactとFirestoreの基本。<br />
参考：:point_down: <br />
[React.js & Next.js超入門のチャプター６を改変：Firestore使用するようにした①](https://qiita.com/atomyah/items/98518b84c5bbd627865b)<br />
[React.js & Next.js超入門のチャプター６を改変：Firestore使用するようにした②](https://qiita.com/atomyah/items/8c2f8a1e554121901d1d) <br />
- フックについてさらっと（任意）<br />
参考：
[ステートフックの利用法](https://ja.reactjs.org/docs/hooks-state.html) 、
[副作用フックの利用法](https://ja.reactjs.org/docs/hooks-effect.html)


でははじめよう。
## firebaseでfirestoreを準備
https://firebase.google.com/ にログインし新規プロジェクト作成。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/d3343395-2fb3-7298-67b0-2173b505fff2.png)

新しくFirestoreデータベースを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/3bccff55-e4e9-3ea0-c7a3-babd1c4da11a.png)

練習なのでテストモードで。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/ac4a2513-e008-0426-c6fd-a671ac6d5e9d.png)


## Gatsbyプロジェクトを準備

### プロジェクト作成
1. コマンドプロンプトでGatsby CLIをインストール `npm install -g gatsby-cli`
2. 任意のディレクトリでGatsbyプロジェクト作成 `gatsby new CRUD-sample`
3. CRUD-sampleというフォルダができてるのでそこに移動
4. `gatsby develop`で開発環境が立ち上がる。`http://localhost:8000/`をブラウズ
5. デフォルトページが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/71fe8b0b-b861-79ad-4a50-9736cd0d03cc.png)

そしたら、srcの下のpagesフォルダにindex.jsファイルがデフォルトで存在するのでそれを、もしくは同じ階層にtest.jsなどを新規作成してもよい。

そこに以下のコードを記述。

```js
// pages/test.js

import React from "react"

export default () => (
  <div>
    <h1>Firestore CRUD App </h1>
    <h2>Item List</h2>
    <h2>Add Item</h2>
  </div>
)
```

`http://localhost:8000/test`にアクセスするとブラウザは以下のように表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/032f5472-8042-463b-4b6b-c3f7198a0537.png)


### GatsbyプロジェクトにFirebase設定
デフォルトでsrcの下にcomponentsフォルダがあるのでそこにitemlist.jsxファイルとfirestore.jsファイルを作成する。Firebaseコンソールに戻り＜/＞のところをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/721a634e-3921-83f9-7e7a-74cb0e723a43.png)

ニックネームを適当に入力してアプリを登録ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/0e5e11e0-0788-7e19-6234-917b353200ad.png)


＜scrip /＞タグ内にあるコンフィグスクリプトをコピーしてどこかに保存しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/fdb6d052-d7c6-a37c-0469-37910449959d.png)


##### Firebase関連モジュールをインストール

コマンドプロンプトでプロジェクトルートにて以下のコマンドでインストール

```bash
$ npm install -g firebase-tools

$ npm install firebase --save
```

完了したらいちおう`gatsby develop`でさっきと同じ画面がブラウズできるか確認。



##### Firebaseコンフィグ設定
components/firebase.jsを開いて、コピーしておいたコンフィグをペースト

```jsx
// components/firebase.js

import firebase from "firebase/app"
import 'firebase/firestore'

  var firebaseConfig = {
    apiKey: "AIzxxxxxxxxxxxxx6HF-8LLhOxxxxxxxxabV9E",
    authDomain: "crud-sample-cxxXxxa.firebaseapp.com",
    databaseURL: "https://crud-sample-cxXxxa.firebaseio.com",
    projectId: "crud-sample-cxXxxa",
    storageBucket: "crud-sample-cxXxxa.appspot.com",
    messagingSenderId: "508xxxxxxx67",
    appId: "1:50xxxxxx967:web:9352xxxxxxxxxx5aeab2"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);

export default firebase
```

import～文とexport default firebaseを忘れずに。

###Firestore接続テスト

test.jsに以下のコードを記述。

```js
// pages/test.js

import React from "react"
import firebase from "./components/firebase"

export default () => {
  firebase
    .firestore()
    .collection("items")
    .add({
      name: "Heineken",
      type: "beer",
      qty: 5,
      description:
        "Pale lager beer with 5% alcohol by volume produced by the Dutch brewing company Heineken International",
    })
    .then(ref => {
      console.log("Added document with ID: ", ref.id)
    })

  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <h2>Add Item</h2>
    </div>
  )
}
```

そしたら`http://localhost:8000/test`をリロード。すると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/f402c58d-c3fc-45f5-7f2d-3ea633d9b959.png)
Firestoreにコレクションが追加されている！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/926daa41-1930-d37d-0c30-71c64c98c4f8.png)

はずです。ここまでミスなければ。

Firestoreに接続し書き込みができることがわかった。test.jsのテストコードは消去する。

## データ閲覧

データをとってきて画面に表示させる。itemlist.jsxを開いて以下のように記述。

```jsx
// components/itemlist.jsx

import React, { useState, useEffect } from "react"
import firebase from "./firebase"
import "../styles/global.css"

const ItemList = () => {
    return (
        
        <table className="tg">
            <tbody>
            <tr>
                <th>名称</th>
                <th>タイプ</th>
                <th>数</th>
                <th>備考</th>
                <th></th>
            </tr>
            </tbody>
            
            {listItem.map(item => (
            <tbody key={item.id}>
            <tr>
                <td>{item.name}</td>
                <td>{item.type}</td>
                <td>{item.qty}</td>
                <td>{item.description}</td>
                <td>
                    <button>Edit</button>
                    <button>Delete</button>
                </td>
            </tr>
            </tbody>
            ))}
        
        </table>
    )

}

export default ItemList
```

これでまずはカラのテーブルが表示される。

<br />
ステートフックを使うのでuseState, useEffectをインポートする。
Firestoreに接続するのfirebase をインポートする。

```jsx
import React, { useState, useEffect } from "react"
import firebase from "./firebase"
import "../styles/global.css"
・・・略・・・
```

ちなみにスタイルはsrcの下のstylesフォルダのglobal.cssというファイルに記述する（なかったら作る）。ここで使うスタイルは以下の通り。

```css
// styles/global.css

.tg {
  border-collapse: collapse;
  border-spacing: 0;
  border-color: #000000;
}
.tg td {
  font-family: Arial, sans-serif;
  font-size: 14px;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: #000000;
  color: #333;
  background-color: #fff;
}
.tg th {
  font-family: Arial, sans-serif;
  font-size: 14px;
  font-weight: normal;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: #000000;
  color: #333;
  background-color: #f0f0f0;
}

```

そしたらgatsby-browser.jsにインポート文を記述。

```js
// gatsby-browser.js

import "./src/styles/global.css"
```

ではステートフックを使ってFirestoreからデータをもってくるコード。

```jsx
// components/itemlist.jsx

const useItems = () => {
    const [items, setItems] = useState([]);

    useEffect(() => {
        const unsubscribe = firebase
        firebase
          .firestore()
          .collection("items")
          .onSnapshot(snapshot => {
            const data = []
            snapshot.forEach(d => data.push({ // snapshot.doc.mapではだめだった
                id: d.id,
                ...d.data(),
              }));
              setItems(data)
          });
        return () => unsubscribe();
    }, []);

    return items;
};
・・・略・・・
```

###### 【コラム】ステートフック：useStateとuseEffect

この一行
`const [items, setItems] = useState([]);`
これで、例えばReactクラスのコンストラクタ内でステート変数をセットするのと同じこと。こんな感じ↓

```jsx
  constructor(props) {
    super(props);
    this.state = { items: null };
  }
```

フックの場合、変数が二つあるが、“分割代入 (destructuring)” とか呼ばれるらしいが、まあ要はふたつめのsetItems（名前は自由に決められる）は関数変数みたいなものでステート変数Itemsを更新する関数的に使う。こんな感じ↓

```js
setItem({ id: 01, name: "生茶", type: "ソフトドリンク", qty: "8", description: "アサヒビバレッジ製造のお茶" });
```

ざっくりいうと、useState()でステート変数をセット、setItems()でステート変数を更新。

じゃあuseEffect()はなんなの？
それもステート変数を更新する。だけどsetItems()がイベントドリブンぽく使うのに対して、useEffectはコンポーネントをマウントしたとき、コンポーネントが更新されたとき、アンマウントされたとき、いずれの場合も発火して“ハイヨ”って動いてくれる。ライフサイクルを気にせずにそこに書いとけばあとはReactがやってくれるよ～という便利なやつ。とりあえずuseEffectもステート変数を更新。

`return () => unsubscribe();`
のところでは、Firestoreの接続を切る、ということをしている。電気のつけっぱなしはイカンよということであろう。

<br />

ここまでのlistitem.jsx

```jsx
// components/listitem.jsx

import React, { useState, useEffect } from "react"
import firebase from "./firebase"
import "../styles/global.css"

const useItems = () => {
    const [items, setItems] = useState([]);

    useEffect(() => {
        const unsubscribe = firebase
        firebase
          .firestore()
          .collection("items")
          .onSnapshot(snapshot => {
            const data = []
            snapshot.forEach(d => data.push({ // snapshot.doc.mapではだめだった
                id: d.id,
                ...d.data(),
              }));
              setItems(data)
          });
        return () => unsubscribe();
    }, []);

    return items;
};

const ItemList = () => {
    /* useItem() API を listItem変数に格納 */
    const listItem = useItems()

    return (
        
        <table className="tg">
            <tbody>
            <tr>
                <th>名称</th>
                <th>タイプ</th>
                <th>数</th>
                <th>備考</th>
                <th></th>
            </tr>
            </tbody>
            
            {listItem.map(item => ( // listItemに格納されたデータをひとつづつ取り出してitemオブジェクトに格納. Itemごとに＜tr＞＜td＞...＜/td＞＜/tr＞に吐き出す.
            <tbody key={item.id}>
            <tr>
                <td>{item.name}</td>
                <td>{item.type}</td>
                <td>{item.qty}</td>
                <td>{item.description}</td>
                <td>
                    <button>Edit</button>
                    <button>Delete</button>
                </td>
            </tr>
            </tbody>
            ))}
        
        </table>
    )

}

export default ItemList
```

test.js（Firestore接続テストに使ったコードは全部消して）

```js
// pages/test.js

import React from "react"
import ItemList from "../components/itemlist"
import "../styles/global.css"

export default () => {
  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <ItemList />
      <h2>Add Item</h2>
    </div>
  )
}

```

こんなふうに表示される。はず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/4d869813-d125-c6cf-d9c6-c71ed3c9a422.png)


## データ追加

これは簡単。
componentsの下にadditemform.jsxファイルを作成。
以下のようにコードを。

```jsx
//components/additemform.jsx

import React, { useState } from "react"
import firebase from "firebase"

const AddItemForm = () => {

    const [name, setName] = useState("")
    const [type, setType] = useState("")
    const [qty, setQty] = useState(null)
    const [description, setDescription] = useState("")

const onSubmit = e => { // eはevent
    /* 
    preventDefaultでページがリロードされるのを防ぐ
    */
    e.preventDefault()
    firebase
        .firestore()
        .collection("items")
        .add({
            name,
            type,
            qty,
            description,
        })
        //.then でフォームクリア
        .then(() => setName(""), setType(""), setQty(''), setDescription(""))
}

    return (
      <form onSubmit={onSubmit}>
        <input placeholder="Name"
          value={name}
          name="name"
          /*  e.currentTarget.value にインプットされた値が入る */
          onChange={e => setName(e.currentTarget.value)}
          type="text"
        />
        <input placeholder="Type"
          value={type}
          name="type"
          onChange={e => setType(e.currentTarget.value)}
          type="text"
        />
        <input placeholder="Qty"
          value={qty}
          name="qty"
          onChange={e => setQty(e.currentTarget.value)}
          type="number"
        />
        <input placeholder="Description"
          value={description}
          name="description"
          onChange={e => setDescription(e.currentTarget.value)}
          type="text"
        />
        <button>Submit</button>
      </form>
    )

 }
export default AddItemForm
```


test.jsは以下のように編集。

```js
// pages/test.js

import React from "react"
import ItemList from "../components/itemlist"
import AddItemForm from '../components/additemform' //追加
import "../styles/global.css"

export default () => {
  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <ItemList />
      <h2>Add Item</h2>
     <AddItemForm /> //追加
    </div>
  )
}
```

では書き込んでみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/38abc5d9-869f-9230-845b-45847bd5700d.png)

Submitボタンをクリック。

リロードしなくともすぐにページに反映される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/9bacaa4d-18e8-b7cb-7df7-5dd50bc9e021.png)

Firestoreにも反映されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/4d479b0a-b4a0-a409-c8b0-5ade77c017e3.png)


## データ削除

これも簡単。

itemlist.jsxを編集する。

HTMLのテーブルにDeleteボタンがあると思う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/12b466d6-d8e0-3444-9241-88dac0d6c18c.png)

deleteItem関数を作ってそのDeleteボタンに割り当てるだけ。

```jsx
// components/itemlist.jsx
・・・略・・・
const deleteItem = (id) => {  // 追加
    firebase
      .firestore()
      .collection("items")
      .doc(id)
      .delete()
}
・・・略・・・
            {listItem.map(item => (
            <tbody key={item.id}>
            <tr>
                <td>{item.name}</td>
                <td>{item.type}</td>
                <td>{item.qty}</td>
                <td>{item.description}</td>
                <td>
                    <button>Edit</button>
                    <button onClick={() => deleteItem(item.id)}>Delete</button>  // 更新
                </td>
            </tr>
・・・略・・・
```

<br />

## データ更新

これがややこしい！

３つのステップがある。
まずは更新用のupdateitemコンポーネントを表示・非表示させるためのフラッグをオフオンする機能
２つめ、更新したいitemデータを特定する機能
最後、実際のデータ更新。

ではcomponentsフォルダにupdateitem.jsxファイルを作成。そして以下のコードを。

```jsx
// components/updateitem.jsx

import React from "react"

const UpdateItem = () => {
  return (
    <>
      <h2>Update Item</h2>
      <form>
        <label htmlFor="Update Item">Update Item:</label>
        <input type="text" name="name" />
        <input type="text" name="type" />
        <input type="number" name="qty" />
        <input type="text" name="description" />
        <button>Update</button>
        <button>Cancel</button>
      </form>
    </>
  )
}
export default UpdateItem
```

test.jsを編集

```js
import React, { useState } from "react" // 変更
import ItemList from '../components/itemlist'
import AddItemForm from '../components/additemform'
import UpdateItem from "../components/updateitem" // 追加
import "../styles/global.css"


export default () => {
  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <ItemList />
      <h2>Add Item</h2>
      <UpdateItem /> // 追加
      <AddItemForm />
    </div>
  )
}

```

つぎに以下のコードをtest.jsに追加する。２つのステート変数をセットするもの。

```js
// pages/test.js

・・・略・・・
export default () => {
  /* 初期化するためのカラ変数配列 */
  const initialItemState = [
    { id: null, name: "", type: "", qty: "", description: "" },
  ]
  /* currentItemステート変数をセット */
  const [currentItem, setCurrentItem] = useState(initialItemState)

  /* 編集モードフラッグになるステート変数をセット */
  const [editing, setEditing] = useState(false)
・・・略・・・
```

currentItemは「いま選んでいるItem」を格納するステート変数。initialItemState は初期化のためだけのもの。つまりcurrentItemを何も選んでいないステート（状態）にするためのものでカラ。
editingは編集モードかそうでないかのフラグboolean。初期値はfalse。


#### 編集モード

ということでまずは編集モードを操作する。editingフラグがtrueの場合はupdateitem.jsxコンポーネントを表示し、falseの場合は同コンポーネントを非表示にする、という機能を追加していく。ではitemlist.jsxを編集する。

```jsx
// components/itemlist.jsx

・・・略・・・
const ItemList = ({ editItem }) => {... // 変更

  <button onClick={() => editItem()}>Edit</button>　// 変更

  ...}
・・・略・・・
```


HTMLのテーブルにEditボタンがあると思う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/12b466d6-d8e0-3444-9241-88dac0d6c18c.png)

そこにeditItem()関数を割り当ててる。editItem()関数はtest.jsに書くので、その返り値をプロパティとして貰ってくるために`const ItemList = ({ editItem }) =>`という具合に{ editItem }プロパティを記述。

EditボタンをクリックするとeditItem()関数を呼びそこで編集モードフラッグeditingをtrueにする。editItem()関数はあとでtext.jsに書く。

つぎにupdateitem.jsxを以下のように編集。

```jsx
// components/updateitem.jsx
・・・略・・・
const UpdateItem = ({setEditing}) => { //変更

  return (...
    <button onClick={()=>setEditing(false)}>Cancel</button> // 変更
  ...)
・・・略・・・
```

setEditing()関数をCancelボタンに割り当ててる。setEditing()はtest.jsにセットしたステートフック`const [editing, setEditing] = useState(false)`の分割代入された変数２つめ`setEditing`のこと。。
CancelボタンをクリックするとsetEditing()を呼び編集モードフラッグeditingをfalseにする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/94a3bca6-0cef-c0fe-3cc4-7b78610e54a7.png)


そしたらtest.jsにeditItem()関数を記述しよう。

```js
// pages/test.js

import React, { useState } from "react"
import ItemList from '../components/itemlist'
import AddItemForm from '../components/additemform'
import UpdateItem from "../components/updateitem"
import "../styles/global.css"

export default () => {

  /* 初期化するためのカラのステート変数s */
  const initialItemState = [
    { id: null, name: "", type: "", qty: "", description: "" },
  ]
  /* currentItemステートにカラのステート変数sを割り当て */
  const [currentItem, setCurrentItem] = useState(initialItemState)

  /* editモードフラッグを初期化 */
  const [editing, setEditing] = useState(false)



  /* editモードをtrueに　*/
  const editItem = (item) => {
    setEditing(true)
  }

  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <ItemList editItem={editItem} />
      <h2>Add Item</h2>
      {editing ? <UpdateItem setEditing={setEditing}/> : <AddItemForm />} // 変更. ternary operation（三項演算子）
    </div>
  )
}
```

![editmode.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/5cbc0946-32ce-41f2-b54a-cf96a4e6eb34.gif)

これで編集モードの切り替えができた。



#### 「いま選んでいるItem」

「いま選んでいるItem」とはアイテムリストで該当ItemのEditボタンをクリックしたItemのこと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/e9065978-25fd-e030-6893-45fe0a9d11ef.png)

currentItemをもってくる機能を追加しよう。updateitem.jsxを以下のように編集。

```jsx
// components/updateitem.jsx

import React, { useState, useEffect } from "react"; // 変更

const UpdateItem = ({ setEditing, currentItem }) => {

  const [item, setItem] = useState(currentItem); // 追加

  useEffect(() => { // 追加
    setItem(currentItem);
    console.log("useEffectが渡したcurrentItem: ", currentItem);
  }, [currentItem]);

  const onSubmit = e => { // 追加
    e.preventDefault();
    console.log("onSubmitで渡されたidとitems", { item });
  };

  return (
    <>
      <h2>Update Item</h2>
      <form onSubmit={onSubmit}> // 変更
        <label htmlFor="Update Item">Update Item:</label>
        <input type="text" name="name" value={item.name} />
        <input type="text" name="type" value={item.type} />
        <input type="number" name="qty" value={item.qty} />
        <input type="text" name="description" value={item.description} />
        <button>Update</button>
        <button onClick={() => setEditing(false)}>Cancel</button> // 変更
      </form>
    </>
  );
};
export default UpdateItem;

```

itemlist.jsxのEditボタンのところを以下のように編集。

```jsx
// components/itemlist.jsx

・・・略・・・
<button onClick={() => editItem(item)}>Edit</button>
・・・略・・・
```


test.jsのeditItem()関数と`<UpdateItem />`コンポーネントを編集。

```js
// pages/test.js

export default () => {...

  const editItem = (item) => {
    setEditing(true)
    setCurrentItem({ // 追加
      id: item.id,
      name: item.name,
      type: item.type,
      qty: item.qty,
      description: item.description,
    })
  }
  return (...
<UpdateItem setEditing={setEditing} currentItem={currentItem} /> // 変更
```

したらEditボタンをクリックして現れたUpdateフォームでUpdateボタンをクリックしてみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/5f6d94b1-dd28-c738-3258-64ea56df2826.png)


選んだ（該当ItemのEditボタンをクリックした）Itemの名前や数量や備考などのデータがコンソールログに吐かれている。

これでだいたい完成。

#### データ更新する関数

あとはFirestoreに更新命令をするだけなので簡単。

test.jsにupdateItem関数をつくる。また`<UpdateItem />`にプロパティを加える。

```js
// pages/test.js

・・・略・・・
  /* firestoreのデータを更新 */
  const updateItem = ({ currentItem }, updatedItem) => { // 追加
    console.log(
      "Firestoreで更新するデータ:　",
      updatedItem,
      currentItem.id
    );
    //editフラグをfalseに
    setEditing(false)
    firebase
      .firestore()
      .collection("items")
      .doc(currentItem.id)
      .update(updatedItem);
  };

  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <ItemList editItem={editItem} />
      <h2>Add Item</h2>
      {editing ? <UpdateItem setEditing={setEditing} currentItem={currentItem} updateItem={updateItem} /> : <AddItemForm />} // 更新
    </div>
  )
}
```


最後。
updateitem.jsxにonChangeイベントハンドラーを。

```jsx
// components/updateitem.jsx

・・・略・・・

    const onSubmit = e => {
        e.preventDefault();
        console.log("onSubmitで渡されたidとitems", { item });
        updateItem({ currentItem }, item); // 追加
    };

    
    const onChange = e => { // 追加
        const { name, value } = e.target;
        setItem({ ...item, [name]: value });
    };
    

    return (
        <>
        <h2>Update Item</h2>
        <form onSubmit={onSubmit}>
            <label htmlFor="Update Item">Update Item:</label>
            <input type="text" name="name" value={item.name} onChange={onChange} /> // 変更
            <input type="text" name="type" value={item.type} onChange={onChange} /> // 変更
            <input type="number" name="qty" value={item.qty} onChange={onChange} /> // 変更
            <input type="text" name="description" value={item.description} onChange={onChange} /> // 変更
            <button>Update</button>
            <button onClick={()=>setEditing(false)}>Cancel</button>
        </form>
        </>
    )
}
export default UpdateItem
```

## コード最終形

もし一部うまくいかなくても、この最終形でちゃんと動く。Dont Worry❢

test.js

```js
// pages/test.js

import React, { useState } from "react"
import firebase from "../components/firebase";
import ItemList from '../components/itemlist'
import AddItemForm from '../components/additemform'
import UpdateItem from "../components/updateitem"
import "../styles/global.css"

export default () => {

  /* 初期化するためのカラ変数配列 */
  const initialItemState = [
    { id: null, name: "", type: "", qty: "", description: "" },
  ]
  /* currentItemステート変数をセット */
  const [currentItem, setCurrentItem] = useState(initialItemState)

  /* 編集モードフラッグステート変数をセット */
  const [editing, setEditing] = useState(false)



  /* editモードをtrueにしてcurrentItemにEditボタンを押下したitemを格納　*/
  const editItem = (item) => {
    setEditing(true)
    setCurrentItem({
      id: item.id,
      name: item.name,
      type: item.type,
      qty: item.qty,
      description: item.description,
    })
  }

  /* firestoreのデータを更新 */
  const updateItem = ({ currentItem }, updatedItem) => {
    console.log(
      "Firestoreで更新するデータ:　",
      updatedItem,
      currentItem.id
    );
    //editフラグをfalseに
    setEditing(false)
    firebase
      .firestore()
      .collection("items")
      .doc(currentItem.id)
      .update(updatedItem);
  };

  return (
    <div>
      <h1>Firestore CRUD App </h1>
      <h2>Item List</h2>
      <ItemList editItem={editItem} />
      <h2>Add Item</h2>
      {editing ? <UpdateItem setEditing={setEditing} currentItem={currentItem} updateItem={updateItem} /> : <AddItemForm />}
    </div>
  )
}
```

itemlist.jsx

```jsx
// components/itemlist.jsx

import React, { useState, useEffect } from "react"
import firebase from "./firebase"
import "../styles/global.css"


const useItems = () => {
    const [items, setItems] = useState([]);

    useEffect(() => {
        const unsubscribe = firebase
        firebase
          .firestore()
          .collection("items")
          .onSnapshot(snapshot => {
            const data = []
            snapshot.forEach(d => data.push({ // snapshot.doc.mapではだめだった
                id: d.id,
                ...d.data(),
              }));
              console.log('◆data１９行目 ', data);
              setItems(data)
          });
        return () => unsubscribe();
    }, []);

    console.log('■items２５行目 ', items);
    return items;
};

const deleteItem = (id) => {
    firebase
      .firestore()
      .collection("items")
      .doc(id)
      .delete()
}


const ItemList = ({editItem}) => {

    /* useItem() API を listItem変数に格納 */
    const listItem = useItems()
    console.log('■listItem３２行目 ', listItem);
    return (
        
        <table className="tg">
            <tbody>
            <tr>
                <th>名称</th>
                <th>タイプ</th>
                <th>数</th>
                <th>備考</th>
                <th></th>
            </tr>
            </tbody>
            
            {listItem.map(item => (
            <tbody key={item.id}>
            <tr>
                <td>{item.name}</td>
                <td>{item.type}</td>
                <td>{item.qty}</td>
                <td>{item.description}</td>
                <td>
                    <button onClick={() => editItem(item)}>Edit</button>
                    <button onClick={() => deleteItem(item.id)}>Delete</button>
                </td>
            </tr>
            </tbody>
            ))}
        
        </table>
    )

}

export default ItemList
```

additemform.jsx

```jsx
// components/additemform.jsx

import React, { useState } from "react"
import firebase from "firebase"

const AddItemForm = () => {

    const [name, setName] = useState("")
    const [type, setType] = useState("")
    const [qty, setQty] = useState(null)
    const [description, setDescription] = useState("")

const onSubmit = e => { // eはevent
    /* 
    preventDefaultでページがリロードされるのを防ぐ
    */
    e.preventDefault()
    firebase
        .firestore()
        .collection("items")
        .add({
            name,
            type,
            qty,
            description,
        })
        //.then でフォームクリア
        .then(() => setName(""), setType(""), setQty(''), setDescription(""))
}

    return (
      <form onSubmit={onSubmit}>
        <input placeholder="Name"
          value={name}
          name="name"
          /*  e.currentTarget.value にインプットされた値が入る */
          onChange={e => setName(e.currentTarget.value)}
          type="text"
        />
        <input placeholder="Type"
          value={type}
          name="type"
          onChange={e => setType(e.currentTarget.value)}
          type="text"
        />
        <input placeholder="Qty"
          value={qty}
          name="qty"
          onChange={e => setQty(e.currentTarget.value)}
          type="number"
        />
        <input placeholder="Description"
          value={description}
          name="description"
          onChange={e => setDescription(e.currentTarget.value)}
          type="text"
        />
        <button>Submit</button>
      </form>
    )

 }
export default AddItemForm

```

updateitem.jsx

```jsx
// components/updateitem.jsx

import React, { useState, useEffect } from "react";

const UpdateItem = ({setEditing, currentItem, updateItem }) => {

    const [item, setItem] = useState(currentItem);

    useEffect(() => {
        setItem(currentItem);
        console.log("useEffectが渡したcurrentItem: ", currentItem);
    }, [currentItem]);
    
    const onSubmit = e => {
        e.preventDefault();
        console.log("onSubmitで渡されたidとitems", { item });
       // updateItem({ currentItem }, item);
    };

    
    const onChange = e => {
        const { name, value } = e.target;
        setItem({ ...item, [name]: value });
    };
    

    return (
        <>
        <h2>Update Item</h2>
        <form onSubmit={onSubmit}>
            <label htmlFor="Update Item">Update Item:</label>
            <input type="text" name="name" value={item.name} onChange={onChange} />
            <input type="text" name="type" value={item.type} onChange={onChange} />
            <input type="number" name="qty" value={item.qty} onChange={onChange} />
            <input type="text" name="description" value={item.description} onChange={onChange} />
            <button>Update</button>
            <button onClick={()=>setEditing(false)}>Cancel</button>
        </form>
        </>
    )
}
export default UpdateItem
```

global.css

```css
// styles/global.css


.tg {
  border-collapse: collapse;
  border-spacing: 0;
  border-color: #000000;
}
.tg td {
  font-family: Arial, sans-serif;
  font-size: 14px;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: #000000;
  color: #333;
  background-color: #fff;
}
.tg th {
  font-family: Arial, sans-serif;
  font-size: 14px;
  font-weight: normal;
  padding: 10px 5px;
  border-style: solid;
  border-width: 1px;
  overflow: hidden;
  word-break: normal;
  border-color: #000000;
  color: #333;
  background-color: #f0f0f0;
}
```


# 本の宣伝
### Gatsbyバージョン５>>>>改訂２版

**前編**の『Gatsby５前編ー最新Gatsbyでつくるコーポレートサイト』と**後編**の『Gatsby５後編ー最新GatsbyとmicroCMSでつくるコーポレートサイト《サイト内検索機能付き》』を合わせ、次のようなデモサイトを構築します。
→ **https://yah-space.work**
<hr />


静的サイトジェネレーターGatsby**最新バージョン５**の基本とFile System Route APIを使用して動的にページを生成する方法を解説。またバージョン５の新機能《**Slicy API**》《**Script API**》《**Head API**》を紹介、実装方法も。《**Gatsby Functions**》での問い合わせフォーム実装やGatsby Cloudへのアップロード方法も！


<a href="https://www.amazon.co.jp/gp/product/B0BXNJTJMY?&linkCode=li3&tag=kokkahasan-22&linkId=93c5e3bf1f226141ddc807300e81b07a&language=ja_JP&ref_=as_li_ss_il" target="_blank"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B0BXNJTJMY&Format=_SL250_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=kokkahasan-22&language=ja_JP" ></a><img src="https://ir-jp.amazon-adsystem.com/e/ir?t=kokkahasan-22&language=ja_JP&l=li3&o=9&a=B0BXNJTJMY" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<a target="_blank" href="https://www.amazon.co.jp/gp/product/B0BXXBDJDJ?&linkCode=ll1&tag=kokkahasan-22&linkId=df7e600968c58c0090cd735681c6732c&language=ja_JP&ref_=as_li_ss_tl">Gatsby５前編ー最新Gatsbyでつくるコーポレートサイト ～基礎の基礎から応用、新機能の導入まで（書籍2,980円）</a>

<br />
<br />

**最新Gatsby５**とmicroCMSを組み合わせてのコーポレートサイト作成手順を解説。《**サイト内検索機能**》をGatsbyバージョン４からの新機能《**Gatsby Functions**》と《**microCMSのqパラメータ**》で実装。また、**SEOコンポーネントをカスタマイズ**して**microCMS APIをツイッターカードに表示**させる**OGPタグ**実装方法も解説。

<a href="https://www.amazon.co.jp/gp/product/B0BXN99WSV?&linkCode=li3&tag=kokkahasan-22&linkId=68f1905bf915f6bd365559a0c65bcdd1&language=ja_JP&ref_=as_li_ss_il" target="_blank"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B0BXN99WSV&Format=_SL250_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=kokkahasan-22&language=ja_JP" ></a><img src="https://ir-jp.amazon-adsystem.com/e/ir?t=kokkahasan-22&language=ja_JP&l=li3&o=9&a=B0BXN99WSV" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<a target="_blank" href="https://www.amazon.co.jp/dp/B0BXN99WSV?_encoding=UTF8&qid=&sr=&linkCode=li2&tag=kokkahasan-22&linkId=f02e2f9c50736e3f074b38b4cd73894a&language=ja_JP&ref_=as_li_ss_il">Gatsby５後編ー最新GatsbyとmicroCMSでつくるコーポレートサイト《サイト内検索機能付き》（書籍 2,790円）</a>

<br />

<br />
##参考：
[Build a CRUD Firestore App in React/Gatsby with Hooks](https://dev.to/vetswhocode/build-a-crud-firestore-app-in-react-gatsby-with-hooks-4ig9)
[ステートフックの利用法](https://ja.reactjs.org/docs/hooks-state.html)
[副作用フックの利用法](https://ja.reactjs.org/docs/hooks-effect.html)