元営業WEBエンジニアのアプリ開発日記

営業出身のWEB系エンジニアが気になったものから作ってはメモを残してくブログ

ApolloでReactからGraphQLのQueryとMutationしてみる

概要

SpringBootとGraphQLで素敵なAPI作成の記事で作った2本のGraphQL APIをReactから呼び出すのにApolloを使ってみる。
Introduction | Apollo ClientにあるEssentialのQuery, Mutationレベルで作ってみる。

もういろいろ書くの面倒になってきたんでこの辺のソースとってきて npm startってたたいてhttp://localhost:3000/search_memberにブラウザでアクセスしてみてくださいな

github.com

Apollo関連をインストール

必要なものをnpmでインストール

npm install apollo-boost react-apollo graphql --save

Queryを実行

名前入力して検索ボタンで検索できるようにしてみる

まずはQueryで呼び出す

基本この書き方が型っぽい。
dataにGraphQLAPIから返ってきた情報が入ってるのでそれをMemberTableコンポーネントに渡してぐるぐる回して表示 /src/containers/MemberTable.jsx

import React from "react";
import gql from "graphql-tag";
import { Query } from "react-apollo";
import MemberTable from "../components/MemberTable";

const GET_MEMBERS_BY_NAME = gql`
query Members($name: String!) {
    members(name: $name) {
      members {
        id
        name
        createdAt
        updatedAt
      }
      errors {
        code
        message
      }
    }
  }  
`;

const SearchMember = ({ ...props }) => (
    <Query query={GET_MEMBERS_BY_NAME} variables={{ name: props.name }} >
        {({ loading, error, data }) => {
            if (loading) return "Loading...";
            if (error) return `Error! ${error.message}`;

            return (
                <MemberTable data={data} />
            );
        }}
    </Query>
);

export default SearchMember;

検索クリックしたときに検索するにする

Queries | Apollo Clientによるとクリック時にquery実装すると<Query>じゃなくてqlient.query()で冗長的になるから、表示したタイミングで実行するのが適切なのかも。
とはいえ初志貫徹で検索ボタン押したら検索できるようにしてみる。

本当はapollo-link-stateを使いたいところですが、今回はstateで簡易的に実現。

  • 入力項目に変更が入ったらstateに保存
  • 検索ボタンクリックで入力項目のstateを検索用のstateに保存
  • 検索用のstateを上で作ったQueryのvariablesに渡してるって寸法

/src/views/SearchMember/SearchMember.jsx

import React from "react";
import SearchByNameBar from "../../components/SearchByNameBar";
import MemberTable from "../../containers/MemberTable";

class SearchMember extends React.Component {
    constructor() {
        super();
        this.state = {
            input: {
                name: ""
            },
            search: {
                name: ""
            }
        }
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleClickSearch = this.handleClickSearch.bind(this);
    }

    handleInputChange(event) {
        this.setState({
            input: {
                name: event.target.value
            }
        });
    }

    handleClickSearch(event) {
        event.preventDefault();
        this.setState({
            search: {
                name: this.state.input.name
            }
        });
    }

    render() {
        return (
            <div>
                <p><b>SearchMember</b></p>
                <SearchByNameBar handleInputChange={this.handleInputChange} handleClickSearch={this.handleClickSearch} />
                <MemberTable data={this.props.data} name={this.state.search.name} />
            </div>
        )
    }
}

export default SearchMember;

Mutationを実行

基本的な方はこんな感じ。
apollo-link-stateをうまく使えば、Mutation実行とともにローカルのデータが更新されて、別コンポーネントのデータを更新とかもできるはず。。。
とりあえず1つのコンポーネントで完結するように書いてみる

/src/components/RegistNameBar.jsx

import React from "react";
import gql from "graphql-tag";
import { Mutation } from "react-apollo";

const ADD_MEMBER = gql`
mutation ReginstMember($name: String!){
    registMember(name: $name) {
      registMember {
        id
        name
      }
      errors {
        code
        message
      }
    }
  }  
`;

function RegistNameBar({ ...props }) {
    let input;
    return (
        <Mutation mutation={ADD_MEMBER}>
            {(addMember, { loading, error, data }) => (
                <div>
                    <form
                        onSubmit={e => {
                            e.preventDefault();
                            addMember({ variables: { name: input.value } });

                            input.value = "";
                        }}
                    >
                        <div>
                            Name  <input type="text" ref={node => { input = node; }} />  <input type="submit" value="Regist" />
                        </div>
                    </form>
                    {data && <p>Regist Success!!</p>}
                    {loading && <p>Loading...</p>}
                    {error && <p>Error :( Please try again</p>}
                </div>
            )}
        </Mutation>
    );
}

export default RegistNameBar;

まとめ

Apolloはリモートとローカルの情報を同期させたり、認証もよろしくできたり、可視化のEngineがあったり可能性は感じるけど、ちゃんと勉強しないと使いこなせなさそう。。。
通勤時間に調べて次のアプリ作成では入れてやろうと思う。