20200328のReactに関する記事は11件です。

Docker + create-react-app v3.4.1でサーバーが立ち上がらないバグ

起きたバグ

react-scripts startでサーバーを立ち上げようとしても

node_1  | yarn run v1.22.4
node_1  | $ react-scripts start
node_1  | ℹ 「wds」: Project is running at http://172.26.0.2/
node_1  | ℹ 「wds」: webpack output is served from 
node_1  | ℹ 「wds」: Content not from webpack is served from /usr/src/app/react-sample/public
node_1  | ℹ 「wds」: 404s will fallback to /
node_1  | Starting the development server...
node_1  | 
node_1  | Done in 25.46s.
docker-react-app_node_1 exited with code 0

というメッセージが表示されサーバーが終了する。

原因

create-react-app v3.4.1で取り込まれた下記の修正

https://github.com/facebook/create-react-app/commit/7e6d6cd05f3054723c8b015c813e13761659759e

process.stdout.isTTYかprocess.env.CIがtrueじゃないと開発用サーバーが停止するらしい。

解決方法

上のコミットのメッセージに

// Gracefully exit when stdin ends
(標準入力が終了したら正常終了するよ)

とあるので標準入力を終わらせないオプションをdocker-compose.ymlに追加。
具体的には下記のオプション

stdin_open: true

と追加するだけ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

転職したいのでReact + Netlify + microCMSを使ってイケてるポートフォリオを作った

本記事でやること

Reactを使ったフロントエンド開発を、今時のサービスを使って簡単に効率よくできる方法を紹介します。
また、ポートフォリオの設計に関してや実装から得られたナレッジ、知っておいたほうがいい実装方法を紹介します。
私のポートフォリオはNetlifyのサーバーに上げているので、実際の挙動などは https://ykonishi.tokyo から確認できます。

ちなみにポートフォリオのソースコードはGitHubにも上げているので、こちらも見ていただけたらと思います。
Yuichi Konishiポートフォリオサイトのソースコード

Reactとは

最近はよくSPA(Single Page Application)という言葉をよく耳にしますが、そのSPAの一つがReactです。
特徴としては、データバインディング仮想DOMComponentの3つがあります。
詳細については各リンクを参照してください。

Netlifyとは

Netlifyは静的サイトのホスティングサービスです。
GitHubやGitLabとも簡単に連携ができるので自動デプロイやJSやAPIを必要としないフォームの作成ができるほか、Netlify Functionsで最近流行りのサーバーレス開発もできるなどフロントエンド開発には十分な機能が揃っています。
ちょっとしたサイトを公開する程度であれば無料枠で使えるので、いろいろなサイトで利用されているのをよく目にします。

microCMSとは

microCMSはウォンタ株式会社が提供するヘッドレスCMSです。
ウォンタ株式会社は過去に話題になったOsushiという投げ銭サービスを運営していた会社ですね :sob:
microCMSは管理画面から入稿したデータをAPI経由で取得できるので、開発者はフロントエンドに専念することができます。
スキーマの定義はユーザーが自由に登録でき、テキストフィールドやテキストエリアはもちろん、リッチエディタや他コンテンツを参照しに行くようなフィールド定義もできます。
こちらのサービスも無料プランがあるのでちょっとしたことを始めるのには十分いいかもしれません。

設計

ページ構成

ページはHome、Profile、Works、Secret Works(鍵をかけたページ)Contactの5つです。

基本的には名前のとおりのコンテンツが入っていますが、各ページの役割はこのような感じです。

HOME:ホーム
Profile:プロフィールページ
Works:実績ページ
Secret Works:パスワード必須の実績ページ
Contact:お問い合わせページ

コンテンツ

profileやworksなどのコンテンツはmicroCMSで管理し、API経由でデータを取得・表示させます。

デプロイ

デプロイはGitHubとNetlifyを連携することで特定のブランチがアップデートされたら実行されるようにします。
また、環境変数も同時に追加されるように設定します。

フォーム

フォームはNetlify FormsというNetlifyの優れたフォーム機能があるので、こちらを使います。
Netlify FormsはSlackとの連携や登録したメールへの通知機能もあるので非常に便利です。

クラス名とCSS

クラス名にはBEM記法を用いることにしました。
ReactにBEMは時代遅れな気もしますが、ページの量が少ないのと手短に実装したかったのでBEMを採用し、CSSで実装しました。

ちなみにReactでCSSを使うときはカプセル化するかモジュール化するかの2通りから選ぶのがベターです。

これからのReactのスタイリングにはStyled Componentsが最高かもしれない
CSSモジュール ― 明るい未来へようこそ

デザイン

カラー

ベースカラーを黒にし、アクセントカラーにロイヤルブルーを入れることでクールで落ち着いた印象に仕上げました。

フォント

フォントは筑紫ゴシックをを用いることでモダンかつシャープな印象を与え、クールに加え美しさを表現しました。

巨大なタイポグラフィ

2020年は巨大なフォントのテキスト配置がトレンドになるそうなので入れてみました。

出処はこちらです
2020年に流行するWebデザインの最新トレンド14個まとめ

参考
https://ykonishi.tokyo/works/xgCARmagX

React実装のナレッジ

React実装にはCreate React Appを使いました。
Reactを使い始めるときのデファクトスタンダードですね。

環境変数

Create React Appには環境変数を提供するためのパイプラインが存在します。
使い方は、React環境が入っているディレクトリ直下に.envファイルを作成し、下記のように設定を書き込んできます。

.env
REACT_APP_API_KEY="xxxxxxxxxxxx"

必ずREACT_APP_を頭に付けてください。

呼び出すときは下記のようになります。

process.env.REACT_APP_API_KEY

React環境をgitで管理する際は、必ず.gitignoreに.envを追記しておくことを忘れないでください。
.envファイルには公にしたくない情報も含まれるので、ホスティングに上げないようにしておきたいからです。

クリーンアップ処理

ReactでAPI処理を走らせるとき、APIが走っているのにもかかわらずページが変わってしまい下記のような警告がでる場合があります。

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

こういった警告が出るときはクリーンアップという処理が必要で、実装はこのようになります。

useEffect(() => {
  let cleanedUp = false;
  const url = new URL('https://xxxxx.com);
  url.pathname = '/api/v1/profile'
  fetch(url)
  .then(res => res.json())
  .then(res => {
    if (!cleanedUp) setProfile(res)
  })
  .catch(error => {
    console.log(error)
  })

  const cleanUp = () => {
    cleanedUp = true;
  };
  return cleanUp;
}, []);

クリーンアップはuseEffect内で使用します。
useEffectはアンマウントされるタイミングでreturn処理が走るので、return cleanUpとすることでアンマウント時にcleanedUp変数がtrueになりprofileが更新されずに済みます。

HTMLコードの有効化

HTMLコードをAPIで取得してそのまま表示するとき、そのままではHTMLタグごと表示されてしまうのでdangerouslySetInnerHTMLというのを使ってHTMLに変換させる必要があります。

実装はこのようになります。

<div
  dangerouslySetInnerHTML={{
    __html: profile.biography
  }}
/>

名前にdangerousとあるように、XSS(クロスサイトスクリプティング)の引き金にもなる可能性があるので使わなくて済む場合は使わないことが推奨されています。

遷移時のページ位置

ReactのようなSPAはページが一から読み込まれるのではなく、一部のDOMが更新されるだけなので遷移時にページ位置が変わることがありません。
ですので、ページが変わったら位置をTOPに戻してやる必要があります。

ScrollToTop.jsx
const ScrollToTop = () => {
  window.scrollTo(0, 0);
  return null;
};
export default ScrollToTop;

このコンポーネントはルーティングの箇所に入れておくことで位置の切り替えができます。

Router.jsx
const Router = () => {
  return (
    <BrowserRouter>
      <Route component={ScrollToTop} />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/profile" component={Profile} />
      </Switch>
    </BrowserRouter>
  );
};

NotFoundページ

NotFoundページの表示は、ルーティングをひと工夫することで実現させることができます。

Router.jsx
const Router = () => {
  return (
    <BrowserRouter>
      <Route component={ScrollToTop} />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/profile" component={Profile} />
        <Route path="*" component={NotFoundPage} />
      </Switch>
    </BrowserRouter>
  );
};

NotFoundPageコンポーネントはどのパスにも当てはまらなかった場合に表示するように一番下に設置し、path="*"とします。
また、必ず一つのコンポーネントが表示されるようにしておかないとNotFoundPageも一緒に表示されてしまうのでSwitchで囲っておきます。

ローディング

ローディングはreact-spinnersというバリエーション豊富で便利なプラグインがあるので、そちらを使いました。
導入も簡単なのでおすすめです。

参考
React Spinners

フォーム作成

フォームはNetlify Formsというのを使うことで簡単に実装できます。

通常のHTMLでは下記のようなコードを入れるだけでいいのですが、Reactの場合はpublic/index.htmlなどにもう一つ別のフォームを入れる必要があります。
Netlifyボットがhtml拡張子以外のファイル内にある、フォームの設定を見に行けないためです。

contact.jsx
<form className="contact-form" name="contact" method="post">
  <input type="hidden" name="form-name" value="contact" />
  <label>
    お名前<span className="required-attention">(必須)</span>
    <input className="contact-form__input" type="text" name="username" required/>
  </label>
  <label>
    Email<span className="required-attention">(必須)</span>
    <input className="contact-form__input" type="email" name="email" required/>
  </label>
  <label>
    会社名
   <input className="contact-form__input" type="text" name="company"/>
  </label>
  <label>
   本文<span className="required-attention">(必須)</span>
   <textarea className="contact-form__textarea field__textarea" name="message" required></textarea>
  </label>
  <button className="contact-form__submit" type="submit">送信</button>
</form>

追加する別のフォームは下記のようになります。
contact.jsxで入れたフィールドはこちらにも必ず追加してください。属性はtypeとnameだけで十分です。

public/index.html
<!-- A little help for the Netlify bots if you're not using a SSG -->
<form name="contact" netlify netlify-honeypot="bot-field" hidden>
  <input type="text" name="name" />
  <input type="email" name="email" />
  <input type="text" name="company" />
  <textarea name="message"></textarea>
</form>

詳しくはこちらを見ていただければと思います。
https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/

メニュー

メニューはreact-springを使って実装しました。

独自仕様が多いので使いこなすのには少し時間がかかります。
react-springはReactでアニメーションを表現するためのプラグインで、Hooks用のプラグインでスポンサーやコントリビューターも充実しているのでHooks時代のデファクトスタンダードになるかもしれません。(もうすでにそうなっているかも :thinking: )

参考
React Spring

最後に

React + Netlify + microCMSを試しに使ってポートフォリオを作ってみた結果、データ管理やサーバーのことをほとんど考えずにフロントエンドに専念できました。

microCMSについては今回初めて使ってみましたが、スキーマを自分で定義してカスタマイズできるほか、RDBのようにほかのコンテンツ(テーブルのようなもの)を参照しに行く定義もできるので非常に使い勝手が良かったです。
メンバーの権限管理機能やほかサービスとのデータ連携機能もあるので、仕事で使うのにもいいかもしれません。

Web制作で頭を悩ましがちなお問い合わせページでは、Netlifyにある便利なフォーム機能があったおかげで一瞬で片付きました。
Webhooks連携や登録したメールアドレスへの通知機能があるのは嬉しいポイントです。

デザイン周りではなるべくシンプルにし、行間、テキストの両端揃え、フォントにも気を配りました。
また、質素な見た目にちょっとした動きを与えているので、飽きさせない工夫も取り入れました。

フロント面でもバックエンド面でもなかなかイケてるポートフォリオサイトが出来上がったのではないでしょうか。

私自身フロントエンド開発に疎いので、ご意見やアドバイスなどあれば大歓迎です!

Yuichi Konishiポートフォリオサイト

参考

Netlify
microCMS

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

転職活動始めるのでReact + Netlify + microCMSを使ってイケてるポートフォリオを作った

本記事でやること

Reactを使ったフロントエンド開発を、今時のサービスを使って簡単に効率よくできる方法を紹介します。
また、ポートフォリオの設計に関してや実装から得られたナレッジ、知っておいたほうがいい実装方法を紹介します。
私のポートフォリオはNetlifyのサーバーに上げているので、実際の挙動などは https://ykonishi.tokyo から確認できます。

ちなみにポートフォリオのソースコードはGitHubにも上げているので、こちらも見ていただけたらと思います。
Yuichi Konishiポートフォリオサイトのソースコード

Reactとは

最近はよくSPA(Single Page Application)という言葉をよく耳にしますが、そのSPAの一つがReactです。
特徴としては、データバインディング仮想DOMComponentの3つがあります。
詳細については各リンクを参照してください。

Netlifyとは

Netlifyは静的サイトのホスティングサービスです。
GitHubやGitLabとも簡単に連携ができるので自動デプロイやJSやAPIを必要としないフォームの作成ができるほか、Netlify Functionsで最近流行りのサーバーレス開発もできるなどフロントエンド開発には十分な機能が揃っています。
ちょっとしたサイトを公開する程度であれば無料枠で使えるので、いろいろなサイトで利用されているのをよく目にします。

microCMSとは

microCMSはウォンタ株式会社が提供するヘッドレスCMSです。
ウォンタ株式会社は過去に話題になったOsushiという投げ銭サービスを運営していた会社ですね :sob:
microCMSは管理画面から入稿したデータをAPI経由で取得できるので、開発者はフロントエンドに専念することができます。
スキーマの定義はユーザーが自由に登録でき、テキストフィールドやテキストエリアはもちろん、リッチエディタや他コンテンツを参照しに行くようなフィールド定義もできます。
こちらのサービスも無料プランがあるのでちょっとしたことを始めるのには十分いいかもしれません。

設計

ページ構成

ページはHome、Profile、Works、Secret Works(鍵をかけたページ)Contactの5つです。

基本的には名前のとおりのコンテンツが入っていますが、各ページの役割はこのような感じです。

HOME:ホーム
Profile:プロフィールページ
Works:実績ページ
Secret Works:パスワード必須の実績ページ
Contact:お問い合わせページ

コンテンツ

profileやworksなどのコンテンツはmicroCMSで管理し、API経由でデータを取得・表示させます。

デプロイ

デプロイはGitHubとNetlifyを連携することで特定のブランチがアップデートされたら実行されるようにします。
また、環境変数も同時に追加されるように設定します。

フォーム

フォームはNetlify FormsというNetlifyの優れたフォーム機能があるので、こちらを使います。
Netlify FormsはSlackとの連携や登録したメールへの通知機能もあるので非常に便利です。

クラス名とCSS

クラス名にはBEM記法を用いることにしました。
ReactにBEMは時代遅れな気もしますが、ページの量が少ないのと手短に実装したかったのでBEMを採用し、CSSで実装しました。

ちなみにReactでCSSを使うときはカプセル化するかモジュール化するかの2通りから選ぶのがベターです。

これからのReactのスタイリングにはStyled Componentsが最高かもしれない
CSSモジュール ― 明るい未来へようこそ

デザイン

カラー

ベースカラーを黒にし、アクセントカラーにロイヤルブルーを入れることでクールで落ち着いた印象に仕上げました。

フォント

フォントは筑紫ゴシックをを用いることでモダンかつシャープな印象を与え、クールに加え美しさを表現しました。

巨大なタイポグラフィ

2020年は巨大なフォントのテキスト配置がトレンドになるそうなので入れてみました。

出処はこちらです
2020年に流行するWebデザインの最新トレンド14個まとめ

参考
https://ykonishi.tokyo/works/xgCARmagX

React実装のナレッジ

React実装にはCreate React Appを使いました。
Reactを使い始めるときのデファクトスタンダードですね。

環境変数

Create React Appには環境変数を提供するためのパイプラインが存在します。
使い方は、React環境が入っているディレクトリ直下に.envファイルを作成し、下記のように設定を書き込んできます。

.env
REACT_APP_API_KEY="xxxxxxxxxxxx"

必ずREACT_APP_を頭に付けてください。

呼び出すときは下記のようになります。

process.env.REACT_APP_API_KEY

React環境をgitで管理する際は、必ず.gitignoreに.envを追記しておくことを忘れないでください。
.envファイルには公にしたくない情報も含まれるので、ホスティングに上げないようにしておきたいからです。

クリーンアップ処理

ReactでAPI処理を走らせるとき、APIが走っているのにもかかわらずページが変わってしまい下記のような警告がでる場合があります。

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

こういった警告が出るときはクリーンアップという処理が必要で、実装はこのようになります。

useEffect(() => {
  let cleanedUp = false;
  const url = new URL('https://xxxxx.com);
  url.pathname = '/api/v1/profile'
  fetch(url)
  .then(res => res.json())
  .then(res => {
    if (!cleanedUp) setProfile(res)
  })
  .catch(error => {
    console.log(error)
  })

  const cleanUp = () => {
    cleanedUp = true;
  };
  return cleanUp;
}, []);

クリーンアップはuseEffect内で使用します。
useEffectはアンマウントされるタイミングでreturn処理が走るので、return cleanUpとすることでアンマウント時にcleanedUp変数がtrueになりprofileが更新されずに済みます。

HTMLコードの有効化

HTMLコードをAPIで取得してそのまま表示するとき、そのままではHTMLタグごと表示されてしまうのでdangerouslySetInnerHTMLというのを使ってHTMLに変換させる必要があります。

実装はこのようになります。

<div
  dangerouslySetInnerHTML={{
    __html: profile.biography
  }}
/>

名前にdangerousとあるように、XSS(クロスサイトスクリプティング)の引き金にもなる可能性があるので使わなくて済む場合は使わないことが推奨されています。

遷移時のページ位置

ReactのようなSPAはページが一から読み込まれるのではなく、一部のDOMが更新されるだけなので遷移時にページ位置が変わることがありません。
ですので、ページが変わったら位置をTOPに戻してやる必要があります。

ScrollToTop.jsx
const ScrollToTop = () => {
  window.scrollTo(0, 0);
  return null;
};
export default ScrollToTop;

このコンポーネントはルーティングの箇所に入れておくことで位置の切り替えができます。

Router.jsx
const Router = () => {
  return (
    <BrowserRouter>
      <Route component={ScrollToTop} />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/profile" component={Profile} />
      </Switch>
    </BrowserRouter>
  );
};

NotFoundページ

NotFoundページの表示は、ルーティングをひと工夫することで実現させることができます。

Router.jsx
const Router = () => {
  return (
    <BrowserRouter>
      <Route component={ScrollToTop} />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/profile" component={Profile} />
        <Route path="*" component={NotFoundPage} />
      </Switch>
    </BrowserRouter>
  );
};

NotFoundPageコンポーネントはどのパスにも当てはまらなかった場合に表示するように一番下に設置し、path="*"とします。
また、必ず一つのコンポーネントが表示されるようにしておかないとNotFoundPageも一緒に表示されてしまうのでSwitchで囲っておきます。

ローディング

ローディングはreact-spinnersというバリエーション豊富で便利なプラグインがあるので、そちらを使いました。
導入も簡単なのでおすすめです。

参考
React Spinners

フォーム作成

フォームはNetlify Formsというのを使うことで簡単に実装できます。

通常のHTMLでは下記のようなコードを入れるだけでいいのですが、Reactの場合はpublic/index.htmlなどにもう一つ別のフォームを入れる必要があります。
Netlifyボットがhtml拡張子以外のファイル内にある、フォームの設定を見に行けないためです。

contact.jsx
<form className="contact-form" name="contact" method="post">
  <input type="hidden" name="form-name" value="contact" />
  <label>
    お名前<span className="required-attention">(必須)</span>
    <input className="contact-form__input" type="text" name="username" required/>
  </label>
  <label>
    Email<span className="required-attention">(必須)</span>
    <input className="contact-form__input" type="email" name="email" required/>
  </label>
  <label>
    会社名
   <input className="contact-form__input" type="text" name="company"/>
  </label>
  <label>
   本文<span className="required-attention">(必須)</span>
   <textarea className="contact-form__textarea field__textarea" name="message" required></textarea>
  </label>
  <button className="contact-form__submit" type="submit">送信</button>
</form>

追加する別のフォームは下記のようになります。
contact.jsxで入れたフィールドはこちらにも必ず追加してください。属性はtypeとnameだけで十分です。

public/index.html
<!-- A little help for the Netlify bots if you're not using a SSG -->
<form name="contact" netlify netlify-honeypot="bot-field" hidden>
  <input type="text" name="name" />
  <input type="email" name="email" />
  <input type="text" name="company" />
  <textarea name="message"></textarea>
</form>

詳しくはこちらを見ていただければと思います。
https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/

メニュー

メニューはreact-springを使って実装しました。

独自仕様が多いので使いこなすのには少し時間がかかります。
react-springはReactでアニメーションを表現するためのプラグインで、Hooks用のプラグインでスポンサーやコントリビューターも充実しているのでHooks時代のデファクトスタンダードになるかもしれません。(もうすでにそうなっているかも :thinking: )

参考
React Spring

最後に

React + Netlify + microCMSを試しに使ってポートフォリオを作ってみた結果、データ管理やサーバーのことをほとんど考えずにフロントエンドに専念できました。

microCMSについては今回初めて使ってみましたが、スキーマを自分で定義してカスタマイズできるほか、RDBのようにほかのコンテンツ(テーブルのようなもの)を参照しに行く定義もできるので非常に使い勝手が良かったです。
メンバーの権限管理機能やほかサービスとのデータ連携機能もあるので、仕事で使うのにもいいかもしれません。

Web制作で頭を悩ましがちなお問い合わせページでは、Netlifyにある便利なフォーム機能があったおかげで一瞬で片付きました。
Webhooks連携や登録したメールアドレスへの通知機能があるのは嬉しいポイントです。

デザイン周りではなるべくシンプルにし、行間、テキストの両端揃え、フォントにも気を配りました。
また、質素な見た目にちょっとした動きを与えているので、飽きさせない工夫も取り入れました。

フロント面でもバックエンド面でもなかなかイケてるポートフォリオサイトが出来上がったのではないでしょうか。

私自身フロントエンド開発に疎いので、ご意見やアドバイスなどあれば大歓迎です!

Yuichi Konishiポートフォリオサイト

参考

Netlify
microCMS

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React入門 ~PropTypes編~

React入門記事、第3弾。
今回はコンポーネントに渡すpropsの使用を補助するものであるPropTypesについて書きました。

PropTypesとは?

Reactで使用するコンポーネントにはpropsを渡して、レンダリング内容に変化をつけたりできます。
Reactで開発する上ではほぼ必須のものであるものの、通常ではどんな値でも受け取ることができてしまいます(TypeScriptであればまた事情が変わってきますが)
そのため、想定と違う値が渡されると予期しない動作をする可能性があります。

それを防ぐため、渡された値の型チェックを行うのがPropTypesです。
元々はReact本体に組み込まれていましたが、バージョン15.5よりprop-typesという別パッケージとして切り分けされました。

インストール

$ yarn add prop-types

今回の使用バージョンは15.7.2です。

使い方

React公式ドキュメントはこちら。
React - Doc - PropTypes を用いた型チェック

基本的な使い方

まずは簡単な使い方から。

App.js
import React from 'react';
import PropsTypesComponent from './PropTypesComponent';

const App = () => {
  return (
    <PropsTypesComponent name="太郎" />
  )
}

export default App;
PropsTypesComponent.js
import React from 'react';
import PropTypes from 'prop-types';

const PropTypesComponent = props => {
  return (
    <h2>Hello {props.name}</h2>
  );
}

PropTypesComponent.propTypes = {
  name: PropTypes.string
};

export default PropTypesComponent;

ライブラリをimport

import PropTypes from 'prop-types';

propsごとのバリデーションを記述

コンポーネント.propTypes = {
 props: PropTypes.バリデーションの種類
}


今回の場合

PropTypesComponent.propTypes = {
  name: PropTypes.string
};

これだけでバリデーションチェックが行われ、無効な値が渡された場合はDevToolsのコンソールにWarningが出力されます。
上記の例では問題ありませんが、例えばPropsTypesComponentに渡しているnameの値を1など、string以外の値にしてみます。
すると、以下のようなWarningがコンソールに出力されているはずです。

warning.png

※1つ注意点として、このバリデーションチェックはパフォーマンス上の理由から開発モードの場合のみ動作します。

バリデーションの種類

数値

PropTypes.number

受け付ける例

1
1.0

文字列

PropTypes.string

受け付ける例

"太郎"
"1"

真偽値

PropTypes.bool

受け付ける例

true
false

一応補足として、あくまで真偽値なので"true"などはダメです。文字列扱いなので、バリデーションに引っ掛かります。

配列

PropTypes.array

受け付ける例

[1, 'A']
[{ id: 'A'}, { id: 'B' }]

配列であれば、その中の値の型までは問わないため非推奨のようです。
中の値の型までチェックする場合はarrayOfを使います。

PropTypes.arrayOf(バリデーションの種類)

// 例
PropTypes.arrayOf(PropTypes.number)

受け付ける例
※PropTypes.arrayOf(PropTypes.number)の場合

[1, 2, 3]

オブジェクト

PropTypes.object

受け付ける例

{ a: 'A', b: 'B' }
など

オブジェクトであれば、その中の値の型までは問わないため非推奨のようです。
中の値の型までチェックする場合はobjectOfshape、もしくはexactを使います。

objectOfは特定の型のみの場合。

PropTypes.objectOf(バリデーションの種類)

// 例
PropTypes.objectOf(PropTypes.number)

受け付ける例
※PropTypes.objectOf(PropTypes.number)の場合

{ a: 1, b: 2 }

shapeは型がバラバラの場合。

PropTypes.shape({
  props: バリデーションの種類,
  props: バリデーションの種類
})

// 例
PropTypes.shape({
  num: PropTypes.number,
  str: PropTypes.string
})

受け付ける例
※上の設定例の場合

{ num: 1, str: '太郎' }

exactも型がバラバラの場合です。
shapeとの違いは、バリデーション定義した以外のものがオブジェクトに追加されているとバリデーションに引っ掛かります。こちらの方がより厳密にpropsをチェックする感じです。

PropTypes.exact({
  props: バリデーションの種類,
  props: バリデーションの種類
})

// 例
PropTypes.exact({
  num: PropTypes.number,
  str: PropTypes.string
})

受け付ける例
※上の設定例の場合

{ num: 1, str: '太郎' }
// ここにこの二つ以外のキー、値があると警告

関数

PropTypes.func

受け付ける例

() => {
  console.log('func');
}

シンボル

PropTypes.symbol

受け付ける例

Symbol()
Symbol('test')

恥ずかしながら自分はシンボルって何?状態だったので調べたのですが、ES6で追加された新しいプリミティブのデータ型だったのですね。

でも、いまいち使い方がよくわからない...。

特定の値のいずれかの場合

PropTypes.oneOf(['値1', '値2'])

// 例
PropTypes.oneOf(['A', 'B', 'C'])

受け付ける例
※上の設定例の場合

'A'
'B'
'C'

いろんなデータ型が渡される可能性がある場合

PropTypes.oneOfType([
  バリデーションの種類,
  バリデーションの種類
])

// 例
PropTypes.oneOfType([
  PropTypes.number,
  PropTypes.string
])

受け付ける例
※上の設定例の場合

1
'1'
'A'

クラスオブジェクト

PropTypes.instanceOf(クラス名)

// 例
PropTypes.instanceOf(Date)

受け付ける例
※上の設定例の場合

new Date()

React Element

PropTypes.element

受け付ける例

<Test /> // 独自定義のコンポーネント
<p>Test</p>

ちなみにelementTypeというものもあるのですが、elementとの違いや、どういう値を想定したものなのかがよくわかりませんでした...。

レンダリングできるもの

PropTypes.node

受け付ける例

1
'A'
['a', 'b']
<p>Test</p>
<Test /> // 独自定義のコンポーネント

数値、文字列、配列、React Elementであればいいようです。
真偽値やオブジェクトはバリデーションに引っ掛かりました。

必須

PropTypes.バリデーションの種類.isRequired

// 例
PropTypes.number.isRequired
PropTypes.any.isRequired // 型は任意

カスタムルール

// カスタムルールの定義
const customProp = (props, propName, componentName) => {
  if (!/test/.test(props[propName])) {
    return new Error(
      'Invalid prop `' + propName + '` supplied to' +
      ' `' + componentName + '`. Validation failed.'
    );
  }
}

// カスタムルールの使用
コンポーネント名.propTypes = {
  props: customProp
}

この例はpropsの値にtestという文字列が含まれているかチェックするものです(公式ドキュメントのコードをベースにしています)

propsのデフォルト値

defaultPropsを使って、propsのデフォルト値を設定できます。
もしそのpropsに値が渡されなかった場合は、このデフォルト値がセットされます。
また、バリデーションチェックと並行して使用している場合は、このデフォルト値セットが行われた後でバリデーションチェックが行われます。

コンポーネント名.defaultProps = {
  props: 
}

// 例
PropTypesComponent.defaultProps = {
  defaultValue: 'default'
}

ずらっとルールを書いていきましたが、実は自分はこのPropTypesをあまり活用できていなかったりします(苦笑)
なので、今回記事を書きながら、あーこういうことできるものだったんだなと改めて思いました。

渡すpropsが多いコンポーネントだとルールを定義するのが大変ではありますが、そのルールを見ればどんな値が渡されてくるのか、またそのコンポーネントを使用するにはどんな値が必要なのか一目瞭然でわかるので、今後活用していきたいですね。

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

問答無用でReactコンポーネントを初期化・リセットする方法

はじめに

とにかくコンポーネントをリセットしたい!マウントからやり直したい!
という時に使えるテクニックを紹介します。

やり方

初期化したいコンポーネントのpropskeyを設定し、keyを更新します。

const Component = () => {
  return (
    <div key={something}>
      ...
    </div>
  )
}

説明

コンポーネントにはライフサイクルがあります。

DZ-97vzW4AAbcZj.jpeg

Reactコンポーネントはとても賢いので、うまく差分を計算してコンポーネントは不要な再描画が走らないようにしています。

そのため、マウント後の更新に関しては基本的にUpdateが適用されます。

一方でkeyはコンポーネントの一意性を保つための仕組みですので、keyが更新されるという事は、まるっきり新しいコンポーネント扱いになります。

そのため、Updateではなく、Mountから新規で描画できる、というわけです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ubuntu系LinuxでReact環境を1分で作る

必要なモジュールインストール

$ sudo apt install -y nodejs
$ sudo apt install -y npm
$ sudo npm install n -g
$ sudo n stable
$ sudo npm install -g yarn
$ yarn global add create-react-app

バージョン確認

$ cat /etc/lsb-release 
DISTRIB_ID=LinuxMint
DISTRIB_RELEASE=19.3
DISTRIB_CODENAME=tricia
DISTRIB_DESCRIPTION="Linux Mint 19.3 Tricia"
$ node -v
v12.16.1
$ npm -v
6.13.4
$ yarn -v
1.22.4

~/.bashrcにパスを追加
export PATH="$PATH:yarn global bin"

$ create-react-app sample
$ cd sample
$ yarn start

http://localhost:3000/
にアクセスしてreactロゴがクルクルしていればok, react!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フロントエンド開発超初学者が贈る、0から始めるReactハンズオン

始めに

レガシー言語しか触ったことのなかった私が2019年12月中旬に初めて世間で一般的に使用されているプログラミング言語に触れて衝撃と感動を覚えてから早3カ月、、、(その時の記事)
あれからも昼休みや休日を利用して様々なイベントへの参加や自己啓発を行い、知識レベルが浦島太郎状態な現状を脱却すべく研鑽を積んできました。
Lambda関数を使用したサーバーレスなシステム作りに携わったり、Spring bootを使った開発技術を学んだり、HTML5プロフェッショナル認定試験の勉強をやったり...(インプットに夢中になってアウトプットさぼってましたすみません)
その一環でReactを使ったハンズオン学習をする機会があり、その時学んだ内容を自分の中に定着させる意味も込めて記事にしようと思ったので、お時間がある方はお付き合いください。

タイトルにもありますが、自分は超初学者ですので、その認識間違ってるよ!ってところなどありましたら優しくコメントくださると嬉しいです!

そもそもReactってなんぞ

Reactとは、webアプリ開発で使用されるJavaScriptのライブラリです。
webアプリのアーキテクチャの話でよく出てくるMVCモデルのView部分の実装に使われることが多いみたいです。

本記事ではこのReactを使って、
① Hello Worldの表示
② ①+αの表示
を作っていきます。

ハンズオンを実施する前に...

本当にCOBOLのようなレガシー言語しか知らなかった私のこの3カ月の経験からですが、私のような本当にまっさらな知識状態でweb開発の勉強がしたい!と思っている方はReactに触れる前にHTML,CSS,JavaScriptの学習をちょろっとでもいいので実施するのをオススメします。
私も昨年の12月Dev fest Tokyoというイベントに行った際angularというJavaScriptのフレームワークでハンズオン体験をする機会があったのですが、よーわからんけどコピペしてたらなんかいい感じのショッピングサイトが出来上がったという感じでした。

というのも、やはりReactの学習と言ってもその中にはHTMLやJavaScriptの概念がふんだんに出てきて、それらは知っている前提で進んでいくのでそもそもの基礎知識がないとただソースをコピペして成果物を作るだけになってしまいます。

とはいっても、コピペだろうと普段自分が使っているwebサービスに似たものが自分の手によって形となって出来上がるのは、それはそれで感動するのでお試しで実施するのはいいかもしれません。
(かくいう私もこのangularのチュートリアルでwebアプリ開発の楽しさに目覚めて今もこうしてモチベーション高く学習しております。)

それでは早速始めていきたいと思います!!

1-1)Reactを使う環境を設定する

Reactの環境導入は自PCにNode.jsというJavaScriptを使う環境をインストールするだけです。
私はMacbookを使用しているので、こちらの方の記事を参考に導入しました。
基本的にはこの通りに実施すれば問題なくReactの環境を導入できるはずです。
この時npmというパッケージ管理ツールが一緒にインストールされるのですが、これを使ってプロジェクト生成をしたりアプリを起動させたりします。

MacにNode.jsをインストール

1-2)成果物のひな形を作る

今回は私のような初心者でもReactを使ったアプリ開発を体系的に理解しやすいように、公式が提供している"create-react-app"というジェネレータを使ってアプリ全体の枠を最初に作ります。
家だけ先に作って、中に入れる家具や家電は後から追加していくイメージですね。

// ひな形の生成
npx create-react-app react-handson

// 生成したプロジェクトへ移動
cd react-handson

ここでひな形生成時にしれっとnpmではなくnpxを使っていますが、npxはnpmパッケージを簡単に実行できるコマンドで、これもnpmをインストールすれば勝手に使えるようになっています。
ここではグローバルインストールせずに1度だけ実行するために使っています。
要は自分のPCにインストールしなくても一瞬だけ借りて使い終わったら速攻で消してくれる便利なコマンドです。

ここで一度以下コマンドでアプリを起動してみましょう。
npm start

するとしばらくしてブラウザが勝手に開くので、以下のような画面がでればOKです。
image.png

2-1)Hello Worldを表示させる

今回は理解しやすくするためにジェネレータによって生成されたコードのうち使わないものは削除します。

rm public/manifest.json 
rm src/*

その後、以下のような最小構成のファイルを作成します。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
src/App.js
import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

export default App;

htmlファイル内の不要な記述も削除して、以下のようにします。

public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

この状態でnpm startすると以下のようにHello Worldが出ると思います。

image.png

2-2)構成と解説

ここまでの時点でディレクトリ構成は以下のようになっているかと思います。

.
├── node_modules    // 外部ライブラリ
├── README.md       // プロジェクトの説明などを記載するファイル
├── package.json    // 依存するライブラリなどを記載するファイル
├── public          // Web上に公開するディレクトリ
│   ├── favicon.ico // ブラウザのタブに出るアイコン
│   └── index.html  // エントリーポイントとなるhtml
└── src             // 開発者が書くコードを置くディレクトリ
    ├── App.js      // ルートコンポーネント
    └── index.js    // Reactのコードのエントリーポイント

ここでHello Worldの描画に直接関わっている人たちは以下の3人
・index.html
・App.js
・index.js

まずindex.htmlですが、ここに記載する内容が実際に画面に表示されます。
今回のソースで言うと、画面に表示される所はここ↓

index.html
<body>
    <div id="root"></div>
</body>

これだけだと、画面には"root"というid属性を持った空のdivタグが表示されるだけです。
ではなぜ画面にHello Worldが表示されたかというと、誰かが"root"というid属性をキーにdivタグの中にHello Worldを入れたのです。
そのHello Worldという中身を持っているのがApp.jsです。

src/App.js
import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

export default App;

App.jsの中には<h1>Hello World</h1>というhtmlタグを戻す関数があり、この戻り値がdivタグに入っている形になります。
そして、このhtmlタグをdivタグの中にぶち込んでくれるのがindex.jsです。
index.jsの中身の内、上3行はただのimportなので実質の中身はこの1行。

src/index.js
ReactDOM.render(<App />, document.getElementById('root'));

ぱっと見よくわからないことをやっているように見えますがこれを日本語にすると、App関数の戻り値をindex.html内にあるid属性が"root"のタグの中にレンダリングするということです。
このレンダリングというのがreactの特徴の一つであり、reactの開発はこのように別々で作ったパーツ(コンポーネント)をレンダリング処理によって別のコンポーネントと組み合わせ、出来上がったものを画面に表示するという流れになります。
機能を追加して行く場合、このコンポーネントやコンポーネントを統合するコードを増やしていく事になります。

3-1)コンポーネントを増やしてみる

コンポーネントの使い方を理解するために、複数のコンポーネントを作成しそれらを組み合わせてみましょう。

新しくsrcディレクトリ配下にcomponentsディレクトリを作成し、そこに以下のようなHello.jsを作成します。

src/components/Hello.js
import React from 'react';

function Hello() {
  return (
    <div>
      <h1>Hello World</h1>
      <p>konnitiwa sekai</p>
    </div>
  );
}

export default Hello;

作成したHelloコンポーネントを画面に表示させるためにApp.jsを修正します。

src/App.js
import React from 'react';
import Hello from './components/Hello'; // Helloコンポーネントをimportする

function App() {
  return <Hello />; // importしたHelloコンポーネントを返すように変更する
}

export default App;

importしたコンポーネントはhtmlタグのようにして使うことができます。

ここまでで、アプリを起動させると以下のようになるはずです。
image.png

次に、引数を渡してそれをコンポーネントに埋め込んだ関数も追加してみます。

作成したcomponentsディレクトリに以下のようなGreet.jsを作成します。

src/components/Greet.js
import React from 'react';

function Greet({ name }) {
  return <p>Hello {name}さん!</p>; // {}で囲うと変数を埋め込むことができる
}

export default Greet;

最後にGreetコンポーネントを呼び出すロジックをApp.jsに追加します。

src/App.js
import React from 'react';
import Hello from './components/Hello';
import Greet from './components/Greet'; // importを追加

function App() {
  return (
    <div>
      <Hello />
      {/* 引数は属性として渡す */}
      <Greet name="Qiita" />
    </div>
  );
}

export default App;

引数の受け渡し方法ですが、属性に値を設定することで渡すことができます。
この時、引数を渡す時の属性名と受け取る時の変数名は同一でないといけないのでご注意ください。
function Greet({ name }) {
<Greet name="Qiita" />

上記の修正が反映した状態でアプリを起動させて、以下が表示されたら成功です。
image.png

最後に

webアプリ開発の'ウェ'の字も知らなかった3ヶ月半前からどっぷりと開発知識の習得に邁進していますが、レガシー業界の住人である私にとってこの業界の技術を学ぶのは本当に刺激的です。
今後も記事にしてアウトプットすることでどんどん知識を自分に落とし込んでいこうと思います。
ちなみに、本当はuseStateを用いた状態管理やらページ遷移やらReduxを用いた状態管理やらも書きたかったのですが、量がすごい事になるのでまた別で書こうと思います。

ここまで読んでくださりありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日々のうんちをシェアするアプリを作る~その1~

うんちしぇあ。作ります

技術の勉強をかねて、何か作りたいなあ、、、なんか良いアイディアないかなあ、、、
そんな思いを日々持っていました。
しかし特に決まらず、ふと彼女に「なんかほしいアプリない?」って聞いてみました。
すると
「毎日何回うんちしたかシェアするアプリかなあ、、、、」

は!?
こいつ天才か!?

僕はそう思いました。
だから作ります。うんちしぇあ。

使う技術

僕はReactというものが書けます。少しですが。
だからNext.jsやってみたいなーっていう安直な理由でNext.js書きます。
あとRailsもかけるからAPI的に使います。

とまあ、明らかに技術力は低いので、間違っているところ・アドバイス・情報共有などあればバンバンください。絶対に受け入れます。一緒に成長していきましょう。女性ならプライベートでの勉強会も

早速作って行くよん

まずはサクッとNext.jsのチュートリアルを終わらせました。。。しかし!!良さが全然わからん。なんか英語だし。

はい。もう良いです。作りながら勉強します。

やっぱうんちしぇあ。だしうんちのコンポーネントからだよね。

pages/unchis/index.ts
// packages
import moment from "moment";

// types
import {IUnchi} from "../../types/unchi";

//components
import { Calendar } from "../components/Calendar";
import {useState} from "react";
import UnchiLists from "../components/UnchiLists";

const today: string = moment().format("YYYY-MM-DD").toString();

const Index = () => {
  const searchUnchi = (date: string): IUnchi => {
    const emptyUnchi: IUnchi = { date: date, count: 0 }
    return emptyUnchi;
  };

  const [unchi, setUnchi] = useState<IUnchi>(searchUnchi(today));
  const handleClick = (date: string) => {
    setUnchi(searchUnchi(date))
  };

  return (
    <>
      <h1>今日のうんち</h1>
      <Calendar onClick={handleClick} />
      <UnchiLists
        {...unchi}
      />
      <style>
        {`
          html {
            width: 100%;
            height: 100%;
          }

          body {
            width: 100%;
            height: 100%;
            position: relative;
          }
        `}
      </style>
    </>
  )
}

export default Index
pages/components/UnchiLists.tsx
import {IUnchi} from "../../types/unchi";
import moment from "moment";
import { Card } from "./Cards"

const UnchiLists = (unchi: IUnchi) => {
  return (
    <Card>
      {moment(unchi.date).format('M月D日')}のうんち
      <ul>
        <li>
          {unchi.count}
        </li>
      </ul>
    </Card>
  )
}

export default UnchiLists

Calendarはネットにあるコピペで使えるデザイン付きのやつを利用させてもらいました。

そして今こんな感じ
image.png

いきなりちょっと本気出しちゃったかな・・・

これで一旦日付に対してうんちの回数を確認できるようになったから次はうんちの登録画面かな。今回は特に難しいことや困ったことがなかったので技術についてあまり触れてませんが、詰まったところなどあれば適宜共有していこうかと思っています。

その2もお楽しみに!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】export default しか無いコンポーネントの記述の仕方

こっちの方がカッコいいよねと思った

import React from 'react';

export default () => <h1>Hello World!!</h1>;

こっちでも勿論OK

import React from 'react';

const Hello = () => <h1>Hello World!!</h1>;

export default Hello;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】AplloClientを使ってGraphQLサーバにアクセスする

概要

  • ReactからGraphQLサーバにアクセスするためのClientライブラリとしてApolloClientがあります
  • 数年前にさわったことがありましたがHooks対応してかなり使いやすくなっていたのでセットアップとQuery/Mutationの叩き方を簡単に紹介します
  • ※ApolloClientは執筆日時点(2020/3/28)でbeta版であるv3系を使っています

事前準備

  • Reactの追加
yarn add react react-dom
  • index.htmlの作成
index.html
<div id="root"></div>
<script src="index.js"></script>
  • index.jsの作成
index.js
import React from 'react';
import { render } from 'react-dom';
import App from './src/App';

render(<App />, document.getElementById('root'));
  • src/App.jsの作成
src/App.js
import React from 'react';

function App() {
  return (
    <div>
      <h1>Hello</h1>
    </div>
  );
}

export default App;
  • 起動
    • 今回は手っ取り早く試すためParcelを使います
npx parcel-bundler index.html

ApolloClientのセットアップ

  • ここからが本題です
  • まずはライブラリを追加します
yarn add @apollo/client graphql
  • 次にGraphQLサーバのアクセス先などを設定します
    • ApolloClientの公式ページに登場するTODOアプリのGraphQLサーバを使っています
    • ブラウザで https://plp0mopxq.sse.codesandbox.io/ にアクセスするとPlaygroundでスキーマなどを確認できます
src/graphql/client.js
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';

export default new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: 'https://plp0mopxq.sse.codesandbox.io/graphql',
  }),
});

  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
    </ApolloProvider>
  );
}

export default App;
  • これでセットアップ完了です

Queryを実行する

  • 実行するQueryを定義するファイルを作成します
    • Todoの全量を取得するGET_TODOSと、指定したIDのTodoを1件取得するGET_TODOの2つを定義します
    • Query実行時に引数を渡す場合はGET_TODOで定義しているような書き方をします
src/graphql/query.js
import { gql } from '@apollo/client';

// 全件取得
export const GET_TODOS = gql`
  query getTodos {
    todos {
      id
      type
    }
  }
`;

// 指定したIDのTODOを1件取得
export const GET_TODO = gql`
  query getTodo($id: String!) {
    todo(id: $id) {
      id
      type
    }
  }
`;

useQuery

  • コンポーネントからGET_TODOSのQueryを呼び出します
  • useQueryを使うとコンポーネントが生成された時にQueryが実行されます
    • 今回のように一覧を取得して表示するようなケースで使うイメージですね
    • 今回実行するGET_TODOSは引数なしですが、ありの場合はuseQueryの第2引数で指定することができます
      • useQuery(GET_TODOS, { variables: { xx: 'xx' } })
src/components/TodoList.js
import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_TODOS } from '../graphql/query';

function TodoList() {
  // Queryを実行
  const { loading, error, data } = useQuery(GET_TODOS);

  if (loading) return <p>...loading</p>;
  if (error) return <p>{error.message}</p>;

  return (
    <div>
      <h2>TodoList</h2>
      {data.todos.map(todo => (
        <p key={todo.id}>{todo.id}: {todo.type}</p>
      ))}
    </div>
  );
}

export default TodoList;
  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';
// importを追加
import TodoList from './components/TodoList';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
      {/* コンポーネントを追加 */}
      <TodoList />
    </ApolloProvider>
  );
}

export default App;

todolist.gif

useLazyQuery

  • IDを指定してTodoを1件取得するGET_TODOをコンポーネントから呼び出します
  • ユーザのアクションなどをきっかけにQueryを実行したい時はuseLazyQueryを使います
  • Query実行時の引数は{ variables: {...} }といった形式でvariablesは固定文言でその中に必要な値をセットします
src/components/Todo.js
import React, { useRef } from 'react';
import { useLazyQuery } from '@apollo/client';
import { GET_TODO } from '../graphql/query';

function Todo() {
  const inputRef = useRef(null);
  // getTodoを呼び出すとQueryが実行される
  const [getTodo, { loading, error, data }] = useLazyQuery(GET_TODO);

  return (
    <div>
      <h2>Todo</h2>
      <div>
        <p>
          <input ref={inputRef} />
          {/* clickされたらgetTodoを実行する */}
          <button onClick={() => getTodo({ variables: { id: inputRef.current.value } })}>
            GET
          </button>
        </p>
        {loading && <p>...loading</p>}
        {error && <p>{error.message}</p>}
        {data && <p>ID: {data.todo.id}, Todo: {data.todo.type}</p>}
      </div>
    </div>
  );
}

export default Todo;
  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';
import TodoList from './components/TodoList';
// importを追加
import Todo from './components/Todo';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
      <TodoList />
      {/* コンポーネントを追加 */}
      <Todo />
    </ApolloProvider>
  );
}

export default App;

todo.gif

Mutationを実行する

  • 実行するMutationを定義するファイルを作成します
    • Todoを追加するADD_TODOを定義します
    • 引数の書き方はQueryの時と同じです
src/query/mutation.js
import { gql } from '@apollo/client';

export const ADD_TODO = gql`
  mutation AddTodo($type: String!) {
    addPlayer(type: $type) {
      id
      type
    }
  }
`;

useMutation

  • コンポーネントからMutationを実行してみます
  • useMutationから取得できるaddTodoを呼び出すとMutationを実行できます
src/components/AddTodo.js
import React, { useRef } from 'react';
import { useMutation } from '@apollo/client';
import { ADD_TODO } from '../graphql/mutation';

function AddTodo() {
  const inputRef = useRef(null);
  const [addTodo, { loading, error }] = useMutation(ADD_TODO);

  return (
    <div>
      <h2>AddTodo</h2>
      {loading && <p>...loading</p>}
      {error && <p>{error.message}</p>}
      <p>
        <input ref={inputRef} />
        {/* クリックしたらaddTodoを実行する */}
        <button onClick={() => addTodo({ variables: { type: inputRef.current.value } })}>
          ADD
        </button>
      </p>
    </div>
  );
}

export default AddTodo;
  • App.jsに反映します
src/App.js
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './graphql/client';
import TodoList from './components/TodoList';
import Todo from './components/Todo';
// importを追加
import AddTodo from './components/AddTodo';

function App() {
  return (
    <ApolloProvider client={client}>
      <h1>Hello</h1>
      <TodoList />
      <Todo />
      {/* コンポーネントを追加 */}
      <AddTodo />
    </ApolloProvider>
  );
}

export default App;

add-todo.gif

  • 最後におまけで、追加したTodoを画面に反映させたいのでTodoListを再取得するボタンを追加します
src/components/TodoList.js
import React from 'react';
import { useQuery } from '@apollo/client';
import { GET_TODOS } from '../graphql/query';

function TodoList() {
  // refetchを追加
  const { loading, error, data, refetch } = useQuery(GET_TODOS);

  if (loading) return <p>...loading</p>;
  if (error) return <p>{error.message}</p>;

  return (
    <div>
      <h2>TodoList</h2>
      {/* クリックするとデータを再取得する */}
      <button onClick={() => refetch()}>REFETCH</button>
      {data.todos.map(todo => (
        <p key={todo.id}>{todo.id}: {todo.type}</p>
      ))}
    </div>
  );
}

export default TodoList;

refetch.gif

まとめ

  • Aplloを使うとloadingの管理などもやってくれるため本当に便利ですね
  • とりあえずuseQuery, useLazyQuery, useMutationの3つが使えればだいたいのことはできると思います
  • これらを使った実装がとても簡単だと実感してもらえていれば幸いです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

'Hoge' is assigned a value but never used no-unused-varsのエラー

エラー

'Hoge' is assigned a value but never used no-unused-vars

原因

Hogeを定義してるのに使ってない

対処法

const Hoge = () => {
    return <p>Hello</p>;
};

return (
    <div>
     <Hoge/>   //ここを追加
    </div>
);

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む