20200403のReactに関する記事は4件です。

[第1章:Gatsbyサイトの理解 編] Gatsby公式ドキュメントを翻訳してみた。

はじめに

このシリーズでは、英語ソースばかりで「Gatsby使ってみたいけど、よくわからない...」という方々のために、公式Docを翻訳(ところどころざっくり要約)していきます。

実際の公式ドキュメントはこちら

(この章では、前章で作成したGatsbyサイトがどのように機能するか理解し、最終的にsurgeというホスティングサービスを利用してデプロイまで完了することがゴールです)

〜〜 以下、翻訳となります 〜〜

1. Gatsbyサイトの構成を理解しましょう

前章では、Gatsby.jsの開発環境を構築し、「hello-world-starter」を利用して高速でサイトを作成しました。この章では、その作成されたサイトの中身に着目していきましょう。

Startersの使い方

0章では、下記のコマンドを使って「hello-world-starter」を利用したサイトを作成しました。

gatsby new hello-world https://github.com/gatsbyjs/gatsby-starter-hello-world

新しくGatsbyサイトを作成する時、あなたは次のようなコマンドを実行することで、既存のStarterを利用したサイトを作成することができます。

gatsby new [作成したいサイトの名前] [利用したいStarterのGithubのURL]

もしも後半のURLを省いた場合、Gatsbyは自動的にこのデフォルトスターターを利用してサイトを生成します。この章では、前回「hello-world-starter」を利用して作成したサイトを使っていきます。

作成されたファイルを見てみましょう

お使いのテキストエディタを開いて、前回作成されたhello-worldというディレクトリを覗いてみましょう!
このようになっているはずです。(VScodeを使用する場合)

hello-world-vscode.png

Gatsbyサイトを構成するファイルを知ろう

まず、/srcディレクトリの中にある/pagesというディレクトリの中を確認しましょう。中にはindex.jsというファイルがあります。このsrc/pages/index.jsというファイルの中に、"Hello world!"という文字列をもつdiv要素を生成するためのコンポーネントが用意されています。

"Hello World"を編集してみましょう

それでは、"Hello world!"というテキストを"Hello Gatsby!"というテキストに書き換えてみましょう。

1 . index.js内の"Hello world"を"Hello Gatsby!"に書き換えて、保存します。
(下記のように、テキストエディタとブラウザのウィンドウを比較できるように並べるとわかりやすいです)

Image from Gyazo

Gatsbyはこのホットプリローディングという技術であなたの開発を加速させます。あなたが開発用サーバを立ち上げている間、Gatsbyで作られたファイルは常に監視されており、ファイルが保存された時に即座にその変更がサイトに反映されるのです。ファイルを保存した後にわざわざページを更新する必要は、もうありません。

2 . 次は、あなたの加えた変更をより見やすいものにします。
src/pages/index.jsの中のコードを、以下のコードに書き換えて保存してみてください。文字の色が紫に変わって、文字の大きさも大きくなるはずです。

import React from "react"
export default () => (
  <div style={{ color: `purple`, fontSize: `72px` }}>Hello Gatsby!</div>
)

(Gatsbyにおけるスタイル変更については、第2章で解説します)

3 . 次に、文字の大きさのみ元に戻して、h1のヘッダーとpタグでページを構成します。

import React from "react"
export default () => (
  <div style={{ color: `purple` }}>
    <h1>Hello Gatsby!</h1>
    <p>What a world.</p>
  </div>
)

more-hot-reloading.png

4 . そして最後に、以下のように書き換えて画像も加えてみましょう。(Unsplashからランダムに画像を取得します)

import React from "react"
export default () => (
  <div style={{ color: `purple` }}>
    <h1>Hello Gatsby!</h1>
    <p>What a world.</p>
    <img src="https://source.unsplash.com/random/400x200" alt="" />
  </div>
)

add-image.png

「え...なんでJavaScriptファイルの中にHTMLが??」

もしあなたがReactやJSXを使い慣れている人であれば、ここは読み飛ばしてもらっても構いません。もしもReactのフレームワーク等を利用したことがないのであれば、このHTMLはJavaScriptファイルの中で一体何をしているんだろうと疑問に思うかもしれません。
これは、JSXと呼ばれる拡張版JavaScriptで、主にReactやVueで使われています。

以下のように、JSXで書かれたファイルは元のJavaScriptファイルとは大きく異なります。しかし、Gatsbyサイトはより書きやすくて読みやすいJSXのファイルを自動的にJavaScriptファイルに変換するツールを兼ね備えているので、安心してJSXを使うことができます。

JSX

import React from "react"
export default () => <div>Hello world!</div>

JavaScript

import React from "react"
export default () => React.createElement("div", null, "Hello world!")

(JSXについてもっと深く知りたい方は、こちらのReact公式ドキュメント(日本語)をどうぞ)

コンポーネントを理解しながらサイトを作ろう

つい先ほどまであなたが作成していたサイトは、1つのpageコンポーネントによって構成されています。
そのコンポーネントとは、一体なんでしょうか。

コンポーネントとは、UI(アプリ内のユーザーが実際に触れる部分、機能)ごとにHTMLやCSS、JavaScriptなどのコードをひとまとめにしたものです。

(GatsbyはReactで生成されているので、このドキュメントで「コンポーネント」が登場したら、それはReactコンポーネントの話をしている思ってください)

わかりやすいように、サイト内にボタンを作成するのを例として扱ってみましょう。
これまで、あなたはCSSファイルの中に新しいクラスを用意し(今回は、.primary-buttonというクラス名を使用)、その中身を書いて、下記のようにそのクラスを適用させたいHTMLファイルの要素に記載していたと思います。

<button class="primary-button">Click me</button>

コンポーネント志向では、代わりにPrimaryButtonというコンポーネントを定義して、それを以下のように使うだけです。

<PrimaryButton>Click me</PrimaryButton>

pageコンポーネントを使ってみよう

src/pages/内のファイルの中で定義されたコンポーネントは、自動的にpageコンポーネントとなります。
どういうことか、早速見てみましょう。

もう既に、あなたはsrc/pages/index.jsというファイルでpageコンポーネントを作成しています。次は、一緒にabout.jsを作ってみましょう。

1 . まずはsrc/pages/内にabout.jsというファイルを新規作成して、次のコードをコピーして保存しましょう。

import React from "react"

export default () => (
  <div style={{ color: `teal` }}>
    <h1>About Gatsby</h1>
    <p>Such wow. Very React.</p>
  </div>
)

2 . http://localhost:8000/about/にアクセスしてみましょう。

gatsby-about-page.png

このように、src/pages/about.jsというファイルにReactコンポーネントを書き込むだけで、あっという間に/aboutでアクセス可能なページを作ることができました。これがpageコンポーネントです。

子コンポーネントを使ってみよう

例えば、HomeページとAboutページの両方のページで同じようなコードが記述されている場合、「繰り返し利用可能な子コンポーネント」を作ることで解決できます。今回作成したページの両方に<h1>が存在しているので、繰り返し使うことのできるHeaderという子コンポーネントを作ってみましょう。

1 . src/componentsという新しいディレクトリを作成し、その中にheader.jsというファイルを作成しましょう。

2 . 以下のコードをそのsrc/components/header.jsの中に記述します。

import React from "react"

export default () => <h1>This is a header.</h1>

3 . 以下のようにabout.jsを編集することでHeaderコンポーネントをインポートし、<h1><Header />に書き換えます。

import React from "react"
import Header from "../components/header"

export default () => (
  <div style={{ color: `teal` }}>
    <Header />
    <p>Such wow. Very React.</p>
  </div>
)

header-component.png

現在ブラウザでは、"About Gatsby"というテキストが"This is a header"に書き換えられていると思います。しかし、Aboutページなので本来は"About Gatsby"と書かれるべきですよね。

4 . src/pages/header.jsの中を以下のように編集します。

import React from "react"

export default props => <h1>{props.headerText}</h1>

5 . src/pages/about.jsの中を以下のように編集します。

import React from "react"
import Header from "../components/header"

export default () => (
  <div style={{ color: `teal` }}>
    <Header headerText="About Gatsby" />
    <p>Such wow. Very React.</p>
  </div>
)

pass-data-header.png

これで、"About Gatsby"というテキストに戻ったはずです。

「propsって何だろう?」

これまで、あなたはいくつかのReactコンポーネントを定義してきました。それらのコンポーネントを静的ではなく動的に使用するためには、props(properties = 所有物)を使って、子コンポーネントにその親となるコンポーネントのデータを渡してあげる必要があるのです。

今回、about.jsという親コンポーネントから、headerTextというprop(親コンポーネントがもつデータ、つまり"About Gatsby")を、子コンポーネントであるHeaderに渡しています。

<Header headerText="About Gatsby" />

一方header.jsでは、親コンポーネントからheaderTextというpropを受け取るために以下のような記述をします。

<h1>{props.headerText}</h1>

このように、JSXファイルでは{}で囲うことによってJavaScriptのコード記法を使うことができます。上では、実際にpropsオブジェクトからheaderTextの値を取得しています。

もし、もうひとつ別のprop(値、データ)を<Header />コンポーネントに渡したい場合は、以下のように書きます。

<Header headerText="About Gatsby" arbitraryPhrase="is arbitrary" />

これで、header.js内で{props.arbitrary}とすれば、arbitraryPhraseの値を取得できるようになります。

6 . こういったコンポーネントの作成がいかに便利なのかを、もうひとつ<Header />コンポーネントをsrc/pages/about.jsに付け足すことで実感しましょう。

import React from "react"
import Header from "../components/header"

export default () => (
  <div style={{ color: `teal` }}>
    <Header headerText="About Gatsby" />
    <Header headerText="It's pretty cool" />
    <p>Such wow. Very React.</p>
  </div>
)

duplicate-header.png

はい、簡単にヘッダーが増えました!
<Header />用のコードを何度も書く必要はありません。propsを使用して、中のデータを渡してあげるだけでOKです。

layoutコンポーネントを使ってみよう

layoutコンポーネントは、Gatsbyサイト内の複数ページで毎回読み込まれる部分をまとめるためのコンポーネントです。例えば、ヘッダーやフッター、サイドバー、ナビゲーションメニュー等です。

(layoutコンポーネントについては、第3章で詳しく解説します)

<Link/>を使ってみよう

<Link />は、Gatsbyからインポートして使用できる便利なコンポーネントの一つです。使い方は簡単です。

1 . src/pages/index.jsを以下のように編集し、<Link />コンポーネントをGatsbyから取得、to=""で行き先を指定しつつ/contactに飛ぶ<Link />コンポーネントを設置します。

import React from "react"
import { Link } from "gatsby"
import Header from "../components/header"

export default () => (
  <div style={{ color: `purple` }}>
    <Link to="/contact/">Contact</Link>
    <Header headerText="Hello Gatsby!" />
    <p>What a world.</p>
    <img src="https://source.unsplash.com/random/400x200" alt="" />
  </div>
)

この段階で生成された"Contact"をクリックすると、このような画面が表示されるはずです。

404-page.png

なぜ、404ページ?
それは、あなたがまだ作成されていないページにアクセスしようとしているからです。

2 . それでは、src/pages/contact.jsに新しい“Contact”ページを作成して、以下のコードを記述しましょう。

import React from "react"
import { Link } from "gatsby"
import Header from "../components/header"

export default () => (
  <div style={{ color: `teal` }}>
    <Link to="/">Home</Link>
    <Header headerText="Contact" />
    <p>Send us a message!</p>
  </div>
)

これで、以下のように"Home"ページと"Contact"ページを行き来できるようになっていれば成功です!

ちなみに、<Link />コンポーネントはそのGatsbyサイトの中での移動でのみ使われます。外部のサイトに移動するリンクを貼りたい場合は、HTMLの<a>タグを使いましょう。

Gatsbyサイトをデプロイしてみよう

Gatsby.jsは静的サイトを生成するモダンなフレームワークであり、複雑なデータベースを動かすようなサーバーを用意する必要はありません。その代わり、gatsby buildというコマンドを使って、静的なHTMLファイルやJavaScriptファイルをホスティングサービスサイト(Webサイトを表示するためのサーバーを使わせてもらえるサービス)でデプロイできる状態に変換する必要があります。

今回は、Surgeを使ってGatsbyサイトをデプロイしてみましょう。

これまでにSurgeを使ったことがない方は、ターミナルで以下のコマンドを実行することでSurgeをインストール・ログインすることができます。

npm install --global surge

# 以下のコマンド実行後、指示通りに入力すればアカウントを作成できます
surge login

Surgeに新規登録またはログインできたら、同じくターミナルで作業中のディレクトリ(hello-worldというディレクトリ)で、gatsby buildを実行してください。

gatsby build

このコマンドは、15〜30秒ほどかかるはずです。
終わったら、以下のようにlsコマンドを使って生成されたファイルを見ることができます。

ls public

それでは、以下のようにして、生成されたファイルを使ってデプロイしましょう!

surge public/

domain: ランダム生成される名前.surge.shというのが表示されたら、enterキーを押しましょう。

ターミナル上の処理が終わって、このような画面が表示されたら、ついにデプロイ成功です!!!

surge-deployment.png

domain:の部分に記載されているwebアドレス(この画像の場合、lowly-pain.surge.sh)にアクセスすれば、あなたが新しく作成したサイトにアクセスできるようになっているはずです。

お疲れさまでした!

参考文献:Gatsby公式ドキュメント

次の章のテーマは、
「GatsbyにおけるCSSの使い方を理解する」です。

お楽しみに!!

[第0章:環境構築編] Gatsby公式ドキュメントを翻訳してみた。

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

Reduxを使ってグローバルステートを扱う方法を全力で分かりやすく説明します。

「Reduxはむずかしそう」

という印象を個人的に持っていましたが、触ってみると思いのほかシンプルだと感じたので、「Reduxを使えるようになりたいけど、なんだか難しそうで手が付けられない」という人向けに、Reduxを使って複数のページで単一のステートを扱う方法を私なりに全力で分かりやすく説明します。
具体的には図と例えを多めに含めます。

なお、Reactを使用することが前提になります。

Reduxを使用したサンプル

サンプル(CodeSandBox)
3cnya-sprx3.gif
各ページで単一のStateを扱っています。
上記のサンプルはTypeScriptと、ページ遷移にreact-router-domを使用しています。

Reduxの使い方

1. Redux(とか)をインストールする

npm i redux react-redux @types/react-redux

または、

yarn add redux react-redux @types/react-redux
  • redux:JavaScriptアプリ用の予測可能な状態コンテナー(公式ドキュメントより)。とりあえずこれは必須です。
  • react-reduxReduxのHooksが使えるようになります。
  • @types/react-reduxReact-Reduxの型定義ファイル。TypeScriptを使わない場合はインストール不要です。

2. グローバルステートをどのようにして扱うかをざっくり把握する

コードを書くことから一旦離れますが、全体像をざっくりとでも把握しておいた方が後の工程で理解が必要となる概念や用語が分かりやすくなるので、前述のサンプルにおいてプラスボタンを押した際の流れと、流れに関わる用語をまとめた下図をご確認ください。
redux.jpg
上図に出てくる用語の意味は私の解釈および流れの説明のために記載したものです。
なので、用語の意味が正しいかどうかはひとまず置いてください:joy_cat:
(「Redux 流れ」や「Redux 図」などで検索すると、より信頼できる図が確認てきます)

上図で出てくる用語は下記の通りです。

  • Store(グローバルステートの倉庫)
  • Action(行動)
  • Type(Actionの内容)
  • Dispatch(送信)
  • Reducer(グローバルステートの処理係)

これらを踏まえて、次項からサンプルのコードのReduxに係る部分を説明します。

3. 画面を作る

特筆することは無いので割愛します。
好きに書いたらいいです。

1.JPG

4. storeを設ける

  • index.tsx
import { createStore } from "redux";
import { Provider } from "react-redux";

// Store(グローバルステートの倉庫)を設ける
const store = createStore(/* Reducer(グローバルステートの処理係。後で設定。 */); 

const rootElement = document.getElementById("root");
render(
  <Provider store={store}> // Storeと各コンポーネントを接続する
    <App />
  </Provider>,
  rootElement
);

上のコードに唐突に現れたProviderは流れ図には無かったものですが、コード内のコメントの通りStoreと各コンポーネントを接続するものと捉えてもらえればよいです。

これで下図のようなStoreができました。
2.JPG
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫を建設した(createStore
  • 倉庫と倉庫の物資を必要とする場所との輸送ルートを設けた(Provider
  • 物資や人員はまだ何も無い

5. Reducerを設定する

Storeを作ったものの、現状Reducerを設定していないのでStoreは空っぽです。
ということでReducerを用意して、createStoreの引数に設定します。

  • /reducers/counterReducer.ts
const counter = (count = 0 /* グローバルステートcountの初期値 */, action: { type: string }) => {
  switch (action.type) {
    case "INCREMENT": // プラスボタンを押した場合
      return count + 1;
    case "DECREMENT": // マイナスボタンを押した場合
      return count - 1;
    default:
      return count;
  }
};
export default counter;

このコードにより、

  • Storeにグローバルステートcount(初期値0)を設定した
  • ステートに対する処理をActionのType別に設定した

ということになります。
Reducerは、画面からDispatch(送信)されたAction(行動)のType(内容)によって、Store(倉庫)にあるcountステートに処理を施します。
caseの文字列"INCREMENT""DECREMENT"については次項で説明します。

Reducerが用意できたのでStoreに設定します。

  • index.tsx
import { createStore } from "redux";
import { Provider } from "react-redux";
import counter from "./reducers/counterReducer"; // 用意したReducerをインポート

const store = createStore(counter); // Reducerを引数に設定

const rootElement = document.getElementById("root");
render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

これでStoreは完成です。
3.JPG
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫に物資を格納した(count = 0
  • 業務内容を定めた人員を雇用した(Switch
  • その人員を倉庫に配属した(createStore(counter)

6. ActionのTypeを設定する

  • 画面が起こすActionはどんなものがあるか
  • それらActionをどのように定義するか

を設定します。

  • /actions/counterAction.ts
// プラスボタンを押すAction 
export const increment = () => {
  return {
    type: "INCREMENT" // このActionのTypeを"INCREMENT"と定義
  };
};

// マイナスボタンを押すAction
export const decrement = () => {
  return {
    type: "DECREMENT" // このActionのTypeを"DECREMENT"と定義
  };
};

この設定を実際の倉庫に係る業務で例えると、倉庫の物資を必要としている場所が、物資の供給を申請する際に使用する申請書を作成する業務にあたるかと思います。
このように捉えると上のコードは、

  • 申請内容別に申請書を作成した
  • そのうちの1種の申請書の名前を"INCREMENT"、もう1種を"DECREMENT"と命名した

ということになります。
画面さんはcountに+1した値が欲しい場合、"INCREMENT"申請書をStoreに勤務しているReducerさんにメールで送信するイメージです。

ここで1つ前に遡って、ReducerのSwitch文の内容をご確認ください。
Switch文がStoreに勤務しているReucerさんの業務内容です。

  • /reducers/counterReducer.ts
  switch (action.type) { // 「申請書の内容をチェックするよ」
    case "INCREMENT": // 「画面さんから送ってもらった申請書は"INCREMENT"だ」
      return count + 1; // 「じゃあcountに+1してcountを画面さんに渡さなきゃ」
    case "DECREMENT":
      return count - 1;
    default:
      return count;
  }

画面さんが"INCREMENT"申請書を送信した例に倣うと、上記コードのコメントの通り、Reducerさんは送信された申請書の内容を確認し、内容に沿った処理を行ないます。

以上でActionと、ActionのTypeを定義できました。
5.JPG

あとはこのActionを画面が持てば、グローバルステートを扱えます。
先の例えで言うならば画面さんが申請書を持つことで、画面さんと倉庫との物流システムが完成します(倉庫に申請書があってもしょうがないですよね)
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫の物資を必要とする場所が、物資の供給を倉庫に申請する申請書を作成した(counterAction.ts

7. ActionをDispatchする

これまでの工程で仕組みを用意できたので、あとは画面からグローバルステートを呼び出したりsetStateするコードを記述することで、「Reduxを使ってグローバルステートを扱う」ことができるようになります。

下記はグローバルステートを扱いたいコンポーネントのコード例です。

  • App.tsx
import React from "react";

const Home = () => {
  return (
    <>
      <h1>Home</h1>
      <p>count: </p>
      <button onClick={() => }>+</button>
      <button onClick={() => }>-</button>
    </>
  );
};

このコードで下図のような画面ができると思います(できなかったらごめんなさい:joy:
5.JPG

count:のコロンの後はcountの値を表示したいですが、何も表示されていません。
なのでStoreからグローバルステートであるcountを取得し、ここにcountの値を表示しましょう。

  • App.tsx
import React from "react";
import { useSelector } from "react-redux"; // useSelectorをインポート

const Home = () => {
  const count = useSelector((count: number) => count); // countをStoreから取得

  return (
    <>
      <h1>Home</h1>
      <p>count: {count}</p> {/* countを表示 */}
      <button onClick={() => }>+</button>
      <button onClick={() => }>-</button>
    </>
  );
};

突然登場したuseSelectorは実際に機能している状態を見れば分かる通り、StoreからStateを取得するHooksです。
これでcount:のコロンの後に、グローバルステートであるcountの値が表示されます。
7.jpg
最後に、ボタンを押すことでグローバルステートの値を増減させ、その値を画面に表示しましょう。
現状はプラスボタンを押してもマイナスボタンを押しても、count: 0の値は変わりません。
下記コードのコメントが入っているコードを追加することで、グローバルステートに対してsetStateできます。

  • App.tsx
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // useDispatchをインポート
import { increment, decrement } from "./actions/counterAction"; // Actionをインポート

const Home = () => {
  const count = useSelector((count: number) => count);
  const dispatch = useDispatch(); // dispatch関数を作成

  return (
    <>
      <h1>Home</h1>
      <p>count: {count}</p>
      {/* Action"increment"をDispatch */}
      <button onClick={() => dispatch(increment())}>+</button>
      {/* Action"decrement"をDispatch */}
      <button onClick={() => dispatch(decrement())}>-</button>
    </>
  );
};

これでプラスボタンを押すとcountの値が+1され、マイナスボタンを押すとcountの値が-1されるようになりました。

ここで改めてプラスボタンを押した際の流れ図をご確認ください。
前の流れ図に比べて、より実装に沿った図にしています。
8.jpg
以上でReduxを使ったグローバルステートを扱う構成が構築できました。

実際の倉庫で例えると、

  • 倉庫から物資を受け取った(useSelector)
  • 倉庫に物資の供給を申請できるようになった(useDispatch)

という体制ができたことになるかと思います。

おわり

今までの説明はReduxのことが全く分からない人向けの説明なので、今までの説明が理解できた場合は、Reduxの公式ドキュメントを読んでReducerに対する認識をアップデートしてください。
以前「公式ドキュメントを読んでも全然意味が分からなかった」という人は、前に比べれば何を言っているか分かる状態になっていることと思います。
というかそうであることを願っています。

Reduxがどうしても理解できなかった場合

もっとシンプルにグローバルステートを扱えるReactNというライブラリがあります。
グローバルなステートをめっちゃシンプルに扱えるReactN - Qiita

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

Reduxを使ってグローバルステートを扱う方法を倉庫に例えて説明します。

Reduxを使えるようになりたいけど、なんだか難しそうで手が付けられない」

という人向けに、Reduxを使ってグローバルステートを扱う方法を倉庫に例えて説明します。

なお、Reactを使用することが前提になります。

Reduxを使用したサンプル

サンプル(CodeSandBox)
3cnya-sprx3.gif
各ページで単一のStateを扱っています。
上記のサンプルはTypeScriptと、ページ遷移にreact-router-domを使用しています。

Reduxの使い方

1. Redux(とか)をインストールする

npm i redux react-redux @types/react-redux

または、

yarn add redux react-redux @types/react-redux
  • redux:JavaScriptアプリ用の予測可能な状態コンテナー(公式ドキュメントより)。とりあえずこれは必須です。
  • react-reduxReduxのHooksが使えるようになります。
  • @types/react-reduxReact-Reduxの型定義ファイル。TypeScriptを使わない場合はインストール不要です。

2. グローバルステートをどのようにして扱うかをざっくり把握する

コードを書くことから一旦離れますが、全体像をざっくりとでも把握しておいた方が後の工程で理解が必要となる概念や用語が分かりやすくなるので、前述のサンプルにおいてプラスボタンを押した際の流れと、流れに関わる用語をまとめた下図をご確認ください。
redux.jpg
上図に出てくる用語の意味は私の解釈および流れの説明のために記載したものです。
なので、用語の意味が正しいかどうかはひとまず置いてください:joy_cat:
(「Redux 流れ」や「Redux 図」などで検索すると、より信頼できる図が確認てきます)

上図で出てくる用語は下記の通りです。

  • Store(グローバルステートの倉庫)
  • Action(行動)
  • Type(Actionの内容)
  • Dispatch(送信)
  • Reducer(グローバルステートの処理係)

これらを踏まえて、次項からサンプルのコードのReduxに係る部分を説明します。

3. 画面を作る

特筆することは無いので割愛します。
好きに書いたらいいです。

1.JPG

4. storeを設ける

  • index.tsx
import { createStore } from "redux";
import { Provider } from "react-redux";

// Store(グローバルステートの倉庫)を設ける
const store = createStore(/* Reducer(グローバルステートの処理係。後で設定。 */); 

const rootElement = document.getElementById("root");
render(
  <Provider store={store}> // Storeと各コンポーネントを接続する
    <App />
  </Provider>,
  rootElement
);

上のコードに唐突に現れたProviderは流れ図には無かったものですが、コード内のコメントの通りStoreと各コンポーネントを接続するものと捉えてもらえればよいです。

これで下図のようなStoreができました。
2.JPG
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫を建設した(createStore
  • 倉庫と倉庫の物資を必要とする場所との輸送ルートを設けた(Provider
  • 物資や人員はまだ何も無い

5. Reducerを設定する

Storeを作ったものの、現状Reducerを設定していないのでStoreは空っぽです。
ということでReducerを用意して、createStoreの引数に設定します。

  • /reducers/counterReducer.ts
const counter = (count = 0 /* グローバルステートcountの初期値 */, action: { type: string }) => {
  switch (action.type) {
    case "INCREMENT": // プラスボタンを押した場合
      return count + 1;
    case "DECREMENT": // マイナスボタンを押した場合
      return count - 1;
    default:
      return count;
  }
};
export default counter;

このコードにより、

  • Storeにグローバルステートcount(初期値0)を設定した
  • ステートに対する処理をActionのType別に設定した

ということになります。
Reducerは、画面からDispatch(送信)されたAction(行動)のType(内容)によって、Store(倉庫)にあるcountステートに処理を施します。
caseの文字列"INCREMENT""DECREMENT"については次項で説明します。

Reducerが用意できたのでStoreに設定します。

  • index.tsx
import { createStore } from "redux";
import { Provider } from "react-redux";
import counter from "./reducers/counterReducer"; // 用意したReducerをインポート

const store = createStore(counter); // Reducerを引数に設定

const rootElement = document.getElementById("root");
render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

これでStoreは完成です。
3.JPG
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫に物資を格納した(count = 0
  • 業務内容を定めた人員を雇用した(Switch
  • その人員を倉庫に配属した(createStore(counter)

6. ActionのTypeを設定する

  • 画面が起こすActionはどんなものがあるか
  • それらActionをどのように定義するか

を設定します。

  • /actions/counterAction.ts
// プラスボタンを押すAction 
export const increment = () => {
  return {
    type: "INCREMENT" // このActionのTypeを"INCREMENT"と定義
  };
};

// マイナスボタンを押すAction
export const decrement = () => {
  return {
    type: "DECREMENT" // このActionのTypeを"DECREMENT"と定義
  };
};

この設定を実際の倉庫に係る業務で例えると、倉庫の物資を必要としている場所が、物資の供給を申請する際に使用する申請書を作成する業務にあたるかと思います。
このように捉えると上のコードは、

  • 申請内容別に申請書を作成した
  • そのうちの1種の申請書の名前を"INCREMENT"、もう1種を"DECREMENT"と命名した

ということになります。
画面さんはcountに+1した値が欲しい場合、"INCREMENT"申請書をStoreに勤務しているReducerさんにメールで送信するイメージです。

ここで1つ前に遡って、ReducerのSwitch文の内容をご確認ください。
Switch文がStoreに勤務しているReucerさんの業務内容です。

  • /reducers/counterReducer.ts
  switch (action.type) { // 「申請書の内容をチェックするよ」
    case "INCREMENT": // 「画面さんから送ってもらった申請書は"INCREMENT"だ」
      return count + 1; // 「じゃあcountに+1してcountを画面さんに渡さなきゃ」
    case "DECREMENT":
      return count - 1;
    default:
      return count;
  }

画面さんが"INCREMENT"申請書を送信した例に倣うと、上記コードのコメントの通り、Reducerさんは送信された申請書の内容を確認し、内容に沿った処理を行ないます。

以上でActionと、ActionのTypeを定義できました。
5.JPG

あとはこのActionを画面が持てば、グローバルステートを扱えます。
先の例えで言うならば画面さんが申請書を持つことで、画面さんと倉庫との物流システムが完成します(倉庫に申請書があってもしょうがないですよね)
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫の物資を必要とする場所が、物資の供給を倉庫に申請する申請書を作成した(counterAction.ts

7. ActionをDispatchする

これまでの工程で仕組みを用意できたので、あとは画面からグローバルステートを呼び出したりsetStateするコードを記述することで、「Reduxを使ってグローバルステートを扱う」ことができるようになります。

下記はグローバルステートを扱いたいコンポーネントのコード例です。

  • App.tsx
import React from "react";

const Home = () => {
  return (
    <>
      <h1>Home</h1>
      <p>count: </p>
      <button onClick={() => }>+</button>
      <button onClick={() => }>-</button>
    </>
  );
};

このコードで下図のような画面ができると思います(できなかったらごめんなさい:joy:
5.JPG

count:のコロンの後はcountの値を表示したいですが、何も表示されていません。
なのでStoreからグローバルステートであるcountを取得し、ここにcountの値を表示しましょう。

  • App.tsx
import React from "react";
import { useSelector } from "react-redux"; // useSelectorをインポート

const Home = () => {
  const count = useSelector((count: number) => count); // countをStoreから取得

  return (
    <>
      <h1>Home</h1>
      <p>count: {count}</p> {/* countを表示 */}
      <button onClick={() => }>+</button>
      <button onClick={() => }>-</button>
    </>
  );
};

突然登場したuseSelectorは実際に機能している状態を見れば分かる通り、StoreからStateを取得するHooksです。
これでcount:のコロンの後に、グローバルステートであるcountの値が表示されます。
7.jpg
最後に、ボタンを押すことでグローバルステートの値を増減させ、その値を画面に表示しましょう。
現状はプラスボタンを押してもマイナスボタンを押しても、count: 0の値は変わりません。
下記コードのコメントが入っているコードを追加することで、グローバルステートに対してsetStateできます。

  • App.tsx
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // useDispatchをインポート
import { increment, decrement } from "./actions/counterAction"; // Actionをインポート

const Home = () => {
  const count = useSelector((count: number) => count);
  const dispatch = useDispatch(); // dispatch関数を作成

  return (
    <>
      <h1>Home</h1>
      <p>count: {count}</p>
      {/* Action"increment"をDispatch */}
      <button onClick={() => dispatch(increment())}>+</button>
      {/* Action"decrement"をDispatch */}
      <button onClick={() => dispatch(decrement())}>-</button>
    </>
  );
};

これでプラスボタンを押すとcountの値が+1され、マイナスボタンを押すとcountの値が-1されるようになりました。

ここで改めてプラスボタンを押した際の流れ図をご確認ください。
前の流れ図に比べて、より実装に沿った図にしています。
8.jpg
以上でReduxを使ったグローバルステートを扱う構成が構築できました。

実際の倉庫で例えると、

  • 倉庫から物資を受け取った(useSelector)
  • 倉庫に物資の供給を申請できるようになった(useDispatch)

という体制ができたことになるかと思います。

おわり

今までの説明はReduxのことが全く分からない人向けの説明なので、今までの説明が理解できた場合は、Reduxの公式ドキュメントを読んでReduxに対する認識をアップデートしてください。
以前「公式ドキュメントを読んでも全然意味が分からなかった」という人は、前に比べれば何を言っているか分かる状態になっていることと思います。
というかそうであることを願っています。

Reduxがどうしても理解できなかった場合

もっとシンプルにグローバルステートを扱えるReactNというライブラリがあります。
グローバルなステートをめっちゃシンプルに扱えるReactN - Qiita

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

GatsbyでFeatherIconsを使う方法

Gatsbyでアイコンフォントを使うときのメモ。

使い方

アイコンフォントをインストールする。

npm install feather-icons --save

yarnの場合

yarn add feather-icons

コンポーネント内でインポート。

{feather.icons.facebook.toSvg()}だけだとHTMLがエスケープされて表示されるので注意。

// src/components/header.js
import React from "react"
import { Link } from "gatsby"
import feather from 'feather-icons'

const Header = () => {
  return (
<header id="header">
  <div className="container">
    <h1 className="logo">サイトタイトル</h1>
    <nav className="header-menu">
      <ul className="nav">
        <li><Link to="/about">メニュー</Link></li>
        <li>
          <Link to="/facebook-url">
            <div dangerouslySetInnerHTML={
              { __html: feather.icons.facebook.toSvg({width: 15,height: 13}) }
            } />
          </Link>
        </li>
      </ul>
    </nav>
  </div>
</header>
  )
}

export default Header

utilを使う場合

utilにコード入れて関数呼び出しの方が便利

// src/utils/feather.js
import React from 'react';
import feather from 'feather-icons';

export default (name, sizeArray) => {
  const featherString = feather.icons[name].toSvg({
    width: sizeArray[0],
    height: sizeArray[1]
  });
  return <div dangerouslySetInnerHTML={{ __html: featherString }} />;
};

コンポーネントで呼び出し

// src/components/header.js
import React from "react"
import feather from '../utils/feather';

const Header = () => {
  return (
<header id="header">
  {feather('facebook', ['30', '30'])}
</header>
  )
}
export default Header
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む