reactjs
React
next.js

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

編集メモ

下記の機構をちゃんと理解する必要がある

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


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

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

githubのアカウントがあればNext.jsの学習サイトで触るうえでの知識が得られたのでお勧めです(fetchの動かし方とかこのサイト読まないとわからんでしょと正直思いました githubのREADME.mdに書いてありました。ちゃんと読めって話ですね・・)。

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

前提とか

  • server側にはrailsアプリを使ってますが必須ではないです
  • isomorphic-unfetchプラグインが必要です

component側

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

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
[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)>

コード

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

post

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

DBの状態(getの時と同じ):before

[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)>

コード

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

DBの状態:after

[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ページ

まとめ

  • getInitialPropsを使う事

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