WIP:React.js/Next.js：fetchでgetとpostする便利メソッドを作る備忘録

# 編集メモ
下記の機構をちゃんと理解する必要がある

* サーバーサイドレンダリング - Next.js
* 非同期処理 - async/await ES6
* props - React.js

<hr>

たかがgetとpostするだけなのに超ハマった。他にもハマっている人いるんじゃないかなぁと思うので、備忘録と言いつつ解説とか加えてます。

ただ、正しくない書き方の可能性が高いので、そこは自己責任で。。

githubのアカウントがあればNext.jsの学習サイトで触るうえでの知識が得られたのでお勧めです（~~fetchの動かし方とかこのサイト読まないとわからんでしょと正直思いました~~ [githubのREADME.md](https://github.com/zeit/next.js#fetching-data-and-component-lifecycle)に書いてありました。ちゃんと読めって話ですね・・）。

URLは一番下にリンク張っています。


# 前提とか
* server側にはrailsアプリを使ってますが必須ではないです
* `isomorphic-unfetch`プラグインが必要です


# component側
getとpostをFetchClientという自作classにまとめてcomponentとして扱う


```javascript:my_app/components/code_test/AjaxClient.js
import fetch from 'isomorphic-unfetch'

class FetchClient{

    static get(page, url, key = 'objects') {
        const body = null
        FetchClient.common('get', page, url, body, key)
    }

    static post(page, url, body, key = 'objects') {
        const stringified_body = JSON.stringify(body)
        FetchClient.common('post', page, url, stringified_body, key)
    }

    static common(method, page, url, body, key = 'objects'){
        page.getInitialProps = async function (){

            // アプリごとに変える必要があるのでheaders引数必要かも
            const headers = {
                'Content-Type': 'application/json'
            }

            const res = await fetch(url, {
                method,
                headers,
                body
            })
            const data = await res.json()

            // デバッグ用
            console.log(data)
            console.log(`Show data fetched. Count: ${data.length}`)

            return {
                [key]: data
            }
        }
    }


}



export default FetchClient
```


`getInitialProps`というNext.jsが提供しているメソッドを使うのがポイントっぽいです。
ページをconstとして造り、そのconstに対してこのメソッドを使用するとpropsをセットできるっぽい。名前はgetとなっているけどsetしてるっぽい。

これを知らないと多分ハマる、かも。

ちなみに、他のコンポーネントから呼ぼうと試行錯誤してみましたが呼び出せませんでした。
公式にはこう書かれています

>*Note: getInitialProps can not be used in children components. Only in pages*

Google翻訳： *注：子コンポーネントではgetInitialPropsを使用できません。 ページ内のみ*

となっているので、そもそも無理そうです。なので、このFetchClientコンポーネントは他のコンポーネントから直接呼び出すのではなく、page経由で間接的に使用するしかなさそうです。


# pages側

それぞれ単体のメソッドを呼び出すだけのpageを作成

## get
ページにアクセスした際にmessagesエンドポイントにgetを行い、response bodyの配列をリストにして表示するだけのpage

### DBの状態
* 三件のmessages

```irb
[65] pry(main)> Message.all
  Message Load (1.2ms)  SELECT "messages".* FROM "messages"
=> [#<Message:0x00007f4c70b40078
  id: 1,
  text: "Good morning",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70b57ea8
  id: 2,
  text: "Good afternoon",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70b57d40
  id: 3,
  text: "Good evening",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>]
[66] pry(main)>

```


### コード
```jsx:my_app/pages/code_test/fetch_get.js
import React from 'react'
import Link from 'next/link'

// 自作component
import FetchClient from '../../components/code_test/FetchClient'

const FetchGet = (props) => (
    <div>
        <ul>
            {props.messages.map(
                message => (
                <li key={message.id}>
                    <Link as={`/p/${message.id}`} href={`/post?id=${message.id}`}>
                        <a>{message.text}</a>
                    </Link>
                </li>
            )
            )}
        </ul>
    </div>
)

FetchClient.get(FetchGet, 'http://localhost:3000/messages', 'messages')

export default FetchGet
```

### ブラウザでアクセス
![get.png](https://qiita-image-store.s3.amazonaws.com/0/107536/87004eb4-a34d-9a1e-7dc2-dfc337ab579d.png)





## post
ページにアクセスした時点で決め打ちの'test'という文字列をmessageエンドポイントにpostし、response bodyの中からidやらtextやらを抜き出してtableに表示するだけのpage

### DBの状態（getの時と同じ）：before
```irb
[65] pry(main)> Message.all
  Message Load (1.2ms)  SELECT "messages".* FROM "messages"
=> [#<Message:0x00007f4c70b40078
  id: 1,
  text: "Good morning",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70b57ea8
  id: 2,
  text: "Good afternoon",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70b57d40
  id: 3,
  text: "Good evening",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>]
[66] pry(main)>

```


### コード
```jsx:my_app/pages/code_test/fetch_post.js
import React from 'react'

// 自作component
import FetchClient from '../../components/code_test/FetchClient'

const FetchPost = (props) => (
    <div>
        <table>
            <thead>
            <tr>
                <th>id</th>
                <th>text</th>
                <th>created_at</th>
            </tr>
            </thead>
            <tbody>
            <tr>
                <td>{props.created_record.id}</td>
                <td>{props.created_record.text}</td>
                <td>{props.created_record.created_at}</td>
            </tr>
            </tbody>
        </table>
    </div>
)


const body = {"text": "test"}
FetchClient.post(FetchPost, 'http://localhost:3000/messages', body, 'created_record')

export default FetchPost
```

### ブラウザでアクセス
![postt.png](https://qiita-image-store.s3.amazonaws.com/0/107536/41f3371a-891b-34a2-b324-f179efc30bc8.png)

### DBの状態：after
```irb
[68] pry(main)> Message.all
  Message Load (1.7ms)  SELECT "messages".* FROM "messages"
=> [#<Message:0x00007f4c70c201c8
  id: 1,
  text: "Good morning",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70c20010
  id: 2,
  text: "Good afternoon",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70c3be78
  id: 3,
  text: "Good evening",
  created_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00,
  updated_at: Tue, 17 Oct 2017 11:13:31 UTC +00:00>,
 #<Message:0x00007f4c70c3bd38
  id: 30,
  text: "test",
  created_at: Sun, 07 Jan 2018 11:29:17 UTC +00:00,
  updated_at: Sun, 07 Jan 2018 11:29:17 UTC +00:00>]
[69] pry(main)>
```

途中のIDが飛んで30になっていますが試行錯誤の名残なので気にしないでください。

4件目が登録されている、post成功。

# 参考にしたwebページ
* [github - Next.js](https://github.com/zeit/next.js)
    * リポジトリ
* [Learning Next.js](https://learnnextjs.com/)　
    * Next.jsの学習サイト
* [Qiita - ES2015以降のJavaScriptでObjectのkeyに変数を使う](https://qiita.com/kmagai/items/95481a3b9fd97e4616c9)
* [Qiita - fetch API でも POST したい！](https://qiita.com/DUxCA/items/801e88462eb5c84af97d)
* [Qiita - ES2015 (ES6)についてのまとめ](https://qiita.com/tuno-tky/items/74ca595a9232bcbcd727)
    * classの参考にさせていただきました
    
# まとめ
* `getInitialProps`を使う事

とりあえずhttps://learnnextjs.com/ を読んだ方が良いですね。。
変な箇所があれば教えてもらえると大変助かります！