20200628のReactに関する記事は7件です。

日本一わかりやすいReact-Redux入門#1~#3

はじめに

この記事は、Youtubeチャンネル『トラハックのエンジニア学習ゼミ【とらゼミ】』の『日本一わかりやすいReact-Redux入門』の学習備忘録です。

前回講座で学んだ React に続き、大規模アプリ開発の必須のライブラリである Redux についても勉強をしていきます。

前回シリーズ『日本一わかりやすいReact入門【実践編】』の記事はこちらからどうぞ。

#1...講座の概要【ECアプリを作ろう】

講座の目標

  • React と Redux で SPA の開発ができるようになる
  • ECアプリの開発方法を学ぶことができる
  • トランザクション処理を入れた決済機能を実装できるようになる

講座の最終成果物

服飾系を商品として取り扱うECアプリを作っていきます。
- 店舗:ユーザー = 1:n(ユニクロやZOZOのように、商品の「出品者」と「購入者」が明確に分かれている設計)
- 店舗は出品、在庫管理、売上管理ができる
- ユーザーは、商品閲覧、カートへの追加、購入、注文履歴の確認ができる

使用技術

  • Javascript(JSX)
  • React
    • react-dom: 画面遷移(ルーティング)を実装
    • react-router: 画面遷移(ルーティング)を実装
  • Redux
    • react-redux: reactとreduxをつなげ、stateを一元管理
    • redux-thunk: データベース通信に欠かせない非同期処理を実装
  • Material-UI
  • Firebase
    • Auth: アカウント認証機能
    • Firestore: データベース
    • Functions: データベースに対する処理(商品の検索機能に使用)
    • Storage: 画像のアップロード
    • Hosting: アプリの本番環境へのデプロイ
  • Stripe(API): アプリへ決済機能を導入するSaaS

講座のAdvanced版

本講座の最後のトピックとなる「決済機能」に関わる部分の講座は、トラハックさんが運営する有料コミュニティ「とらゼミ」から視聴可能です。(そこまではYoutube上で無料公開)

本Qiita記事では無料公開される範囲の講座のみを取り扱います。

Redux に関わる内容については無料範囲で十分に学べると思います。

#2...CRAとFirebaseで環境構築

動画に倣ってreactアプリとFirebaseの設定をすればOKです。

流れは前回講座の「日本一わかりやすいReact入門【実践編】#2...サクッと環境構築しよう」とほぼ同様です。(Redux など、インストールするnpmパッケージには違いがあります)

#3...Fluxフローは絶対に知っておいて

Redux とは?

Reactアプリの state を一元管理するためのライブラリ。

Redux のない純Reactアプリでは、stateはコンポーネント内で定義され、親コンポーネント→子コンポーネントと、バケツリレーの要領で渡されていく。アプリが小さいサイズのときは純Reactでも問題にはならないが、アプリが大きくなってくるについて、バケツリレーがどんどん複雑かつ高階層になる(親→子→孫→ひ孫→...)。

また、特定のコンポーネントで使用しているstateが「そもそもどのコンポーネントで定義されたものか」などの見通しも悪くなり、管理がとても大変になる。

この state 管理問題を解決するために、Reduxが用いられる。Redux を導入することで、アプリ全体の state を保存しておく Storeという場所が作られる。

Reactアプリ側は、このStoreと通信することで、どのコンポーネントからでも state を扱うことができるようになり、無駄なバケツリレーを防ぐことができる。

また、 state自体の定義も全てRedux側で行うことになるので、「このstateどのコンポーネントから来てるんだっけ?」という問題が起こらなくなる。

Fluxフローとは

データフロー設計の1つ。
- 1. データが常に1方向に流れる
- 2. 何らかのイベントをきっかけにデータの状態が変化する(イベント駆動)

Reduxは、「Reactの状態管理をFlux思想で行う」ことを念頭に開発されたライブラリであり、Flux思想で扱われる単語がしばしば用いられる。

Reduxの登場人物

Reduxの中でのデータの受け渡しは、Fluxフローに則り、いくつかの役割に分担されている。

例えば、「現在1という値が格納されているcountという state の値が、画面に描画されているCOUNT_UPボタンが押されることで、2に変更される」というストーリーを通じて、Reduxにおける登場人物を整理する。

Components

いわゆる普通の React のコンポーネント。ReduxのFluxフローにおいては、イベントが最初に走る発火点という位置付け。

COUNT_UPボタンが定義されており、「countの値を+1したい」といった趣旨の命令が実行される記述が、ボタンのonClickイベントとして定義されている。

Container

Store(=stateが保存されている場所)と接続された、特殊なコンポーネント。個々のComponentsの取りまとめ役。

通常の Components は Redux とは直接接続されていないため、いったんContainerが「COUNT_UPボタンが押された」のようなイベントの情報を受け取る必要がある。

Components「countを+1したいです」
Container「分かった。Reduxに聞いてみる」

Action

Containerからの要求を受け、変更をさらに次へ依頼する。Fluxフローにおいては窓口の役割。

変更を依頼すること自体は、dispatchと言う。

Container「countを+1したいらしいです」
Action「分かりました。担当につなぎます(dispatch)」

Reducer

変更を管理する役割。現在の state の状態と、Action から受けた依頼の内容から、実行すべき処理を解釈する。

Action「countを+1したい、とのことです」
Reducer「分かりました」

Reducer「現在のcountの値が1、依頼内容は『countの値を+1したい』だから、countの値を2にすればいいんだな」

Store

状態を保存する倉庫の役割。Reducerの命令により、stateを取り出すor変更する。

Reducer「countの値を2に変更した上で、再度アプリ側に出してください」
Store「分かりました(ここでstateの変更が実行される)」

mapStateToProps・mapDispatchToProps

変更された state を、再度 Containerに渡す

mapStateToProps・mapDispatchToProps「countの値を2に変わったから、Componentsに渡しておいて」
Container「分かりました」

この後、ContainerからComponentsに、変更後のcountが反映される。

Container「countが+1できたみたいよ」
Components「やったぜ」

大切なのは、
- 1. データが常に1方向に流れる
- 2. 何らかのイベントをきっかけにデータの状態が変化する(イベント駆動)
というFlux思想がしっかりと守られている点。例えば、Reducer→Actionのような方向のデータの受け渡しは絶対に起こらない。

おわり

今回はここまで。

次回からは実際にコードを書く内容になっていく予定です。

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

Rails のデータを React から参照するアプリケーション作成 ( Rails + React + MySQL )

はじめに

他の方の記事を参考に自分なりに Rails + React + MySQL の環境を作ってみたので、その時のやり方をまとめました。
上からコピペするだけで動くようにまとめてみたので、はじめてだけどやってみたいという方がいらっしゃれば、試していただきたいです。

コマンド

こちらの項目で実施することは以下となります。
上から順番にターミナルで実行していただければと思います。

  • やること
    • rails アプリケーションの作成
    • webpacker インストール
    • react インストール
    • MVC作成
    • migration
$ rails _5.2.4.2_ new react_sample_app --webpack=react -d mysql

$ cd react_sample_app

# Webpackを有効にする
$ rails webpacker:install

# Reactを有効にする
$ rails webpacker:install:react

# sample モデルを scaffold にて作成
$ rails g scaffold sample title:string body:string

# migrate
$ rails db:create
$ rails db:migrate

※routeなどの細かい設定が面倒だと思ってscaffoldを使いましたが、scaffoldが必須というわけではありません。

View

続いては、Viewの設定をしていきます。

application.html.erb

全体へ反映させたいことをこちらに記載していきます。

  • やること
    • javascript_include_tagjavascript_pack_tag へ変更する
    • view のファイル毎に読み込む javascript(React) を指定するために、<%= yield :javascript %>を追記
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>ReactSampleApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

    <!-- javascript_include_tag を下記へ変更する -->
    <!-- javascript_include_tag 'application', 'data-turbolinks-track': 'reload' -->
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>

    <!-- view のファイル毎に読み込む javascript(React) を指定するために必要 -->
    <%= yield :javascript %>
  </body>
</html>

個別のerbファイル

ここでは例えとして、app/views/samples/index.html.erb上に変更を加えますが、他の画面で実装していただいも何も問題ありません。

  • やること
    • Rails から React へ渡すデータを作成(content_tagにより)
    • view のファイル毎に読み込む javascript(React) を指定する
app/views/samples/index.html.erb
<!-- 省略 -->
<!-- 一番下の行に以下を追記 -->

<!-- content_tag の data 属性を React へ渡す -->
<%= content_tag :div,
                id: "resources-container",
                data: {
                    q: params,
                    resources_path: samples_path,
                }.to_json do %>
<% end %>

<!-- view のファイル毎に読み込む javascript(React) を指定することができる -->
<% content_for :javascript do %>
  <%= javascript_pack_tag 'hello_react' %>
<% end %>

javascript

ここでは、コマンド入力時に作成したapp/javascript/packs/hello_react.jsxを用いますが、個々にファイルを設定していただいて問題ありません。

  • やること
    • Hello コンポーネントの作成
      • props を state に格納する
      • state.name を更新する関数を作成する
      • レンダリングする HTML要素を作成する
    • Rails から読み込みたい & React からレンダリングしたい要素を指定
    • 指定した要素(node)から、data を取得する
    • Hello コンポーネントを呼び出し
      • data を Hello コンポーネントの props として渡す
      • node へ React からレンダリングする
app/javascript/packs/hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

// Hello コンポーネントの作成
class Hello extends React.Component {

  constructor(props) {
    super(props)

    // props を state に格納する
    this.state = {
      q: this.props.q || '',
      resources_path: this.props.resources_path || '',
      name: this.props.name || 'David'
    }
  }

  render() {
    // state.name を更新する関数を作成する
    const setName = e => {
      this.setState({
        name: e.target.value
      })
    }

    // レンダリングする HTML要素を作成する
    return (
        <div>
          <div>Hello {this.state.name}!</div>
          <input type="text" defaultValue={this.state.name} onChange={setName}/>
        </div>
    )
  }
}

document.addEventListener('DOMContentLoaded', () => {
  // Rails から読み込みたい & React からレンダリングしたい要素を指定する
  const node = document.getElementById('resources-container')
  // 指定した要素(node)から、data を取得する
  const data = JSON.parse(node.getAttribute('data'))

  ReactDOM.render(
    // Hello コンポーネントを呼び出し
    // Rails から取得した data を Hello コンポーネントの props として渡す 
    <Hello {...data}/>,
    // node へ React からレンダリングする
    node
  )
})

rails s と webpacker 起動を一括して実行する方法

以下に、scriptsの部分を追記します。
すると、yarn startをターミナル上で実行するたけで、rails s & bin/webpack-dev-serverを実行してくれます。

package.json
{
  "name": "react_sample_app",
  "private": true,
  "dependencies": {
    "@babel/preset-react": "^7.10.1",
    "@rails/webpacker": "5.1.1",
    "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
    "prop-types": "^15.7.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1"
  },
  "devDependencies": {
    "webpack-dev-server": "^3.11.0"
  },

  // 以下を追記する
  "scripts": {
    "start": "rails s & bin/webpack-dev-server"
  }
}

$ yarn start

まとめ

いかがでしたか。
間違いなどがあれば、お手数をおかけしますがご指摘いただけると嬉しいです。

React を理解しているとスマホアプリを作れる React Native への導線が引けるかなーと思い勉強中です。
まずは、Rails と連携していけているアプリケーションを作れるようになりたいなーと思っています。

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

setStateをちょっとだけ工夫した話

最近PRレビューした中でこんな感じのstateがあった。

const start = type === 'start' ? argumentNumber : this.state.start;
const end = type === 'end'? argumentNumber : this.state.end;
this.setState({
 start,
 end,
 info: {
  hoge: 'textChanged'
 }
})

infoの中身は必ず変更されるけど、startとendはtypeによって変更有無が変わる。
なんかsetStateに不要な値入るの気持ち悪いと思いました。

let newState = {
 info: {
  hoge: 'textChanged'
 }
}
if (type === 'start') {
 newState = { ...newState, ...{ start: argumentNumber} }
} else {
 newState = { ...newState, ...{ end: argumentNumber } }
}
this.setState(newState)

こうしたほうが綺麗かも。
ただ変数の中身が動的に変化していくから、多用するとnewStateの中身がカオスになる。
できれば関数に分けたほうがメンテしやすそう。

function newPositionState(type) {
 if (type === 'start') {
  return { start: argumentNumber}
 } else {
  return { end: argumentNumber }
 }
}

let newState = {
 info: {
  hoge: 'textChanged'
 }
}

newState = { ...newState, newPositionState() }
this.setState(newState)

こうすれば読みやすい。
更新対象のstateが増えたとしても、特に問題なさそう。
もっといい方法があれば教えていただきたい。

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

Learning React #3 yarn startは何をしている?

一旦は実行できるようになった最初のReactアプリケーション"hello-world"ですが、実行する時には何やらバックエンドでウニュウニュ動いてますね。
これって何を実行しているんでしょうかね。
探ってみたいと思います。

yarn startは何をしている?

それでは実行時のコンソール出力を見てみましょう。
前回同様にメッセージに括弧付きの番号"()"を付けてから1つ1つ確認していきます。

yarn_start
satom@W1007018N182 MINGW64 ~/src/react-app/hello-world (master)

(1)
$ yarn start

(2)
yarn run v1.22.4

(3)
$ react-scripts start
i 「wds」: Project is running at http://192.168.3.8/
i 「wds」: webpack output is served from
i 「wds」: Content not from webpack is served from C:\Users\satom\src\react-app\hello-world\public
i 「wds」: 404s will fallback to /

(4)
Starting the development server...
Compiled successfully!

(5)
You can now view hello-world in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://192.168.3.8:3000

(6)
Note that the development build is not optimized.
To create a production build, use yarn build.

(1) React開発サーバーの実行

単純に、yarnからReact開発サーバーを実行しています。内部的には(3)で説明している通りreact-script startが実行されています。
何故yarn startが`react-script startになるのかは、別の記事で説明しますね。

(2) 実行したyarnコマンドのバージョン

単純なので割愛します。

(3) React開発サーバー(webpack-dev-server)起動時のステータス表示

内部的には"webpack-dev-server"(wds)という、フロントエンド開発時に使用できるWEBサーバーが起動しているようです。

参考までに、公式のwebpack-dev-serverの情報はこちらです。
https://github.com/webpack/docs/wiki/webpack-dev-server

複数メッセージが表示されていますが、開発サーバー起動時以下の内容をコンソールに出力しています。

  • wds実行時のURLを通知
  • webapckにより生成されたコンテンツのURL
  • webpackに存在しない静的コンテンツのURL
  • 404エラー発生時はエラーとせずに"/"へフォールバックする

このように設定されるのは分かりました。ですが、これってどこで指定されているんでしょうね。これも別の記事で確認結果を書きたいと思います。
今は先に進みましょう。

と、その前に、webpackというキーワードが気になりますので、少しだけ調べてみました。

webpackとは

クライアント側のパッケージツールでした。私たちが作成したJavaScriptファイル(jsファイル)から参照している他のjsファイルやcssファイルやimageファイルをまるっと1つのjsファイルにまとめるためのJavaScriptライブラリです。(イメージファイルはbase64に符号化されて保存されるようです)
これを使うメリットは、

  • ネットワークトラフィックの負荷を軽減
  • レンダリングが高速化
  • モジュール管理が容易

デメリットは、CSSやイメージもパックされるので、デザイナーとの協業で苦労するようです。
とりあえず調べた内容をザックリ紹介しましたが、詳細は別の記事で書きたいと思います。

(4) WDSの起動

先ほど説明したwdsがここで起動されています。

(5) ReactアプリケーションへのアクセスURL

起動したReactアプリケーションにアクセスするためのローカルURLとネットワークURLが表示されます。
ただ、便利なことにwdsが実行されると、デフォルトブラウザが自動起動され、ここに表示されたURLにアクセスしてくれます。

(6) ビルドオプションの注意書き

最後に、開発用ビルドなので最適化されていないよーって通知してくれてます。
最終的に別環境にデプロイする時はyarn buildを使う事で配布用パッケージという形で生成されるようです。

以降の記事では、作成されたReactアプリケーションのプロジェクトファイルを1つ1つ確認していきたいと思います。Reactアプリケーションというのがどのようなファイル構成になっており、どのような原理で動作しているのか、あまり深く入りすぎず、理解できる程度の深度で追ってみたいと思います!

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

TypeScript の何が役に立つのか?

TypeScript を使ってみて何が役に立つか考えてみたことを初心者向けに解説する。

TypeScript ではプログラマーが型を記述する。JavaScript では必要なかった型を記述することによって手間がかかるが、ビルドやエディターが型情報を使ったらプログラマーにとっていろいろと役に立つ

型情報を使うと何が役に立つのか?それは TypeScript を使ってみないと想像がつかないものだった。

TypeScriptのインストール方法

Node.jsをインストールするとnodenpmが使えるようになる。

グローバルで typescript をインストールし%APPDATA%\npmにパスを通す。

> npm -g install typescript

またはnpm initで package.json を作成してから typescript をその場所にインストールする。.\node_modules\.binにパスを通す。

> npm init -y
> npm install typescript

tsc が使えるようになる。

ビルドが型情報を使うと役に立つ

TypeScript のプログラムを JavaScript に翻訳することをビルドと呼ぶ。翻訳と言っても主に付けた型情報を削除するだけだが、TypeScript はビルドするとき型が合わないものをエラーで弾く。

たとえば TypeScript は次のプログラム test.ts をエラーで弾く。

test.ts
function sum(a: number, b: number): number {
  return a + b;
}

sum(1);
sum(1, 2, 3);

数値を 2 つ受け取って数値を返すという関数の型を記述した。このプログラムは数値を 2 つ渡すべき所に数値を 1 つまたは 3 つ渡しているので、正しく動作しない。関数の型が合わないので型エラーになる。


次のようにしてビルドする。

> tsc --init
message TS6071: Successfully created a tsconfig.json file.

> tsc
test.ts:5:1 - error TS2554: Expected 2 arguments, but got 1.
5 sum(1);

test.ts:7:11 - error TS2554: Expected 2 arguments, but got 3.
7 sum(1, 2, 3);

TypeScript のビルド方法は特殊だ。tsconfig.json でオプションを設定するが、tsc コマンド上でファイルを指定すると tsconfig.json を読み込まない。そこで tsconfig.json を作って tsc でビルドする。


JavaScript のプログラマーは目で見てわかるから型検査はいらないと思うかもしれない。わざわざ型定義を記述してビルドするのが手間だ。

短いプログラムのときは TypeScript の必要性を感じない。しかし、長いプログラムのとき型検査が役に立つことを感じる。


プログラムの一部を変更したとする。TypeScript がなければ、長いプログラムのどこに影響するかプログラマーが目で見て探さなければならない

TypeScript は影響する箇所を型エラーとして報告する。プログラマーが探さなくていい。

エディターが型情報を使うと役に立つ

エディターはマイクロソフト製の Visual Studio Code を使う。マイクロソフト製の TypeScript と相性がいい。

Visual Studio Code 1

この例は文字型の name プロパティと数値型の age プロパティを持つオブジェクトとして Person 型を記述した。

型情報がわかるので、プロパティの補完が正確に効く

JavaScript のプログラムでもプロパティを補完するが、可能性のある候補を多めに挙げて正確ではない。


npmパッケージを使うときもパッケージの型定義が役に立つ。

Visual Studio Code 2

パッケージのhttp-link-headerとは別に型定義ファイル@types/http-link-headerをインストールするとhttp-link-headerをimportできるようになる。

header.Link型にget2がないので型エラーが表示される。JavaScriptではエラーが表示されない。

header.Link.Reference型にrelとuriがあるので補完が正確に効く。JavaScriptも補完が効くが正確ではない。

http-link-headerの型定義はF12を押すと型定義ファイルの場所へジャンプして調べられる。

React と相性がいいらしい

次の型エラーのない React のプログラム test.tsx を考える。Visual Studio Code で編集する。

React 1

const MyButton の text_bb を書き間違えてみると間違えた所に型エラーが表示された。

React 2

ReactDOM.render の text_aa を書き間違えてみると間違えた所に型エラーが表示された。

React 3

test.tsx を test.js に変えて型情報を削除して同じ箇所を書き間違えてみたらエラーが表示されなかった。React は TypeScript がないとエラーを発見できない

React を使うのなら TypeScript を使うべきだ。React の何が役に立つのかという疑問はあるが。

行の関係性に注目してみた

プログラムの不具合は行と行の関係性が原因で発生する。

行と行の関係性は行数の二乗の数だけある。それで長いプログラムほど不具合発生の関係性が増大する。

プログラムが長くなるとすべての関係性を調べる負担をプログラマーが負いきれなくなる。

TypeScript は型を調べることですべての関係性が合っているかを自動的に調べられる。それでプログラマーの負担を軽減できる。

まとめ

型情報があることでプログラミング検査の自動化が進む。

短いプログラムのときは型の記述とビルドに手間がかかるだけだが、型の記述は長いプログラムで真価を発揮する。

TypeScript の型検査はエラーの発生箇所を自動的に調べるので、プログラミングが楽になる。

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

Remote ContainersでReactのDocker開発環境を構築

概要

  • 更新内容

    • ※emvファイルをdocker-compose内に変更しました。
  • 環境

    • React
      • TypeScript
      • storybook
    • DockerImage
      • node:13.12.0-slim
  • 開発環境は下記のリポジトリにあるのでお好きに使って下さい。

    • react-docker-development-environment
      • そのままpushをすると、私のリポジトリの内容が変更されてしまうので、pushとかは自分のリポジトリを作成して行って下さい。
      • 使用して気に入ったのなら☆を付けてくれると励みになります。

前提

  • Docker

  • Remote Containers

  • なぜDockerでReactの開発環境を作るのか

    • プロジェクトでnodeのバージョンなどを固定できる
    • ホストマシンにNodeを入れなくてOK
    • プロジェクトメンバーが増えてもコンテナを開くだけで環境構築が完了
  • なぜRemote ContainersでReactの開発環境を作るのか

    • Remote Containersならコンテナを開くときに拡張機能をインストールしてくれるのでプロジェクトで環境を統一可能。
    • VSCodeの設定もプロジェクト単位で統一可能。(フォーマッターなど)
    • プロジェクトメンバーが増えてもコンテナを開くだけで環境構築が完了(超重要)

使い方

  1. react-docker-development-environmentを git clone
  2. VSCodeの拡張機能でRemote Containersを追加する
  3. VSCodeで「Ctrl + Shift + p 」と入力
  4. Remote-Containers: Open Folder in Container と入力
  5. プロジェクト配下のdockerという名前のフォルダを選択する(.devcontainer配下が展開される)
  6. 実際に開発環境として使用する場合、ssh-agentを有効にしないとPushできません
    1. VSCode Remote ContainersでGitにSSH接続でpushする (Windows)

CreateReactApp

今回の環境はReactでTypeScriptを使用し、storybookの環境構築を済ませた環境になっております。
自分で環境構築を行う場合、以下の手順でできます。

  1. Nodeを使用可能なコンテナを起動
    1. Docker Hub - Node
  2. コンテナ内で以下のコマンドを実行

    npm install -g create-react-app
    ## TypeScriptでReactのApplication構築を行う(テスト環境も配備)
    create-react-app sample --typescript
    cd sample
    # Reactのプロジェクトフォルダ配下で実行することでstorybookの環境構築を行う
    npx -p @storybook/cli sb init --type react_scripts
    

実装した内容

実際に環境変数で使用している内容の解説をします。

Dockerfile
# ベースとするDockerImage
FROM node:13.12.0-slim
WORKDIR /usr/app
# パッケージの依存関係に関するファイルをホストからDocker内にコピー
COPY package.json yarn.lock tsconfig.json ./
# yarn install する
RUN yarn
# yarn install 後、全ファイルをコピーすることで、パッケージ関連がコピーされ、キャッシュが効くようになり高速化
COPY . .
CMD [ "yarn", "start" ]
docker-compose.yaml
    # compose fileのフォーマットバージョン(バージョンによって使える記法が違う)
    version: '3.7'
    services:
        node:
            build:
            # 使用するDockerFileのDirectoryを指定
            context: ../../
            # 使用するDockerFileの名前を指定(Dockerfile.Nodeはdocker-compose.yamlと同じディレクトリにあるが、contextで2階層上を使用すると指定している、だからわざわざdocler/.devcontainer/ という風にしてDockerfile.Nodeを指定)
            dockerfile: docker/.devcontainer/Dockerfile.Node
            volumes:
            # ローカルのディレクトリが接続(マウント)する作業ディレクトリを指定(srcフォルダをusr/app/src にマウントしている)
            - ../../src:/usr/app/src
            # 外部に対して公開するポート番号
            ports:
            - "3000:3000"
            # コンテナを起動させ続けるか管理するフラグ
            tty: true
            # 環境によってはDockerでHotReloadが効かなくなるので環境変数により有効化する
            environment:
              - CHOKIDAR_USEPOLLING=true
devcontainer.json
    {
        "name": "Existing Docker Compose (Extend)",
        "dockerComposeFile": [
            "docker-compose.yml"
        ],
        "service": "node",
        "workspaceFolder": "/usr/app/", // VSCodeのTerminalが開くディレクトリを設定
        "settings": {
            "terminal.integrated.shell.linux": null,
            "files.trimTrailingWhitespace": true, // ファイル保存時に行末の空白自動削除
            "editor.formatOnSave": true // ファイル保存時に自動フォーマットが実行される
        },
        // Remote Containers で起動した際にVSCodeの拡張機能をインストール
        "extensions": ["eamodio.gitlens","chakrounanas.turbo-console-log","visualstudioexptteam.vscodeintellicode","esbenp.prettier-vscode","dbaeumer.vscode-eslint","shardulm94.trailing-spaces","msjsdiag.debugger-for-chrome","christian-kohler.path-intellisense","usernamehw.errorlens","gizak.shortcuts","coenraads.bracket-pair-colorizer-2"],
        // Remote Containers で起動した際にgitをインストール
        "postCreateCommand": "apt-get update && apt-get install -y git",
    }

苦戦した点

  1. コンテナ内でホットリロードが効かない

    1. docker-compose内でCHOKIDAR_USEPOLLINGを設定することで解決
  2. yarn start が遅い

    1. 一番苦労した点、コンテナ内だと5分かかることもありました。(ローカルだと10秒で終わる)
    2. 原因はyarn がキャッシュされておらず一々node_modulesを作成してたから
      1. 下記の記事の内容で解決しました
        1. Dockerfile入門する
    3. Docker for Macは遅いらしいのでMacの方は注意が必要らしいです。
      1. フロントエンドの開発環境に Docker は不要(少なくともMacでは)

経緯

  1. Excelや社内Wikiなどで書かれた環境構築資料の通りにやってもエラーが出て環境構築できない
  2. 結果、環境構築だけで時間が消える
  3. プロジェクトメンバーが増える度に同じ時間が無駄になる
  4. 溜まっていく疲労と虚無感
  5. Remote Containersなら秒で終わる!
  6. これからはコンテナが入っていれば環境構築が秒で終わる
  7. みんな幸せ

参考資料

Reactの開発環境をDockerで整えよう

Docker Composeの仕様について混乱しがちな箇所を整理した

VSCode Remote ContainersでGitにSSH接続でpushする (Windows)

Dockerfile入門する

docker-composeでreact環境構築(更新版)

Todo-App-Client

Dockerizing a React App

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

Remote ContainerでReactのDocker開発環境を構築

概要

  • 更新内容

    • ※emvファイルをdocker-compose内に変更しました。
  • 環境

    • React
      • TypeScript
      • storybook
    • DockerImage
      • node:13.12.0-slim
  • 開発環境は下記のリポジトリにあるのでお好きに使って下さい。

    • react-docker-development-environment
      • そのままpushをすると、私のリポジトリの内容が変更されてしまうので、pushとかは自分のリポジトリを作成して行って下さい。
      • 使用して気に入ったのなら☆を付けてくれると励みになります。

前提

  • Docker

  • Remote-Container

  • なぜDockerでReactの開発環境を作るのか

    • プロジェクトでnodeのバージョンなどを固定できる
    • ホストマシンにNodeを入れなくてOK
    • プロジェクトメンバーが増えてもコンテナを開くだけで環境構築が完了
  • なぜRemote-ContainerでReactの開発環境を作るのか

    • Remote-Containerならコンテナを開くときに拡張機能をインストールしてくれるのでプロジェクトで環境を統一可能。
    • VSCodeの設定もプロジェクト単位で統一可能。(フォーマッターなど)
    • プロジェクトメンバーが増えてもコンテナを開くだけで環境構築が完了(超重要)

使い方

  1. react-docker-development-environmentを git clone
  2. VSCodeの拡張機能でRemote Containerを追加する
  3. VSCodeで「Ctrl + Shift + p 」と入力
  4. Remote-Containers: Open Folder in Container と入力
  5. プロジェクト配下のdockerという名前のフォルダを選択する(.devcontainer配下が展開される)
  6. 実際に開発環境として使用する場合、ssh-agentを有効にしないとPushできません
    1. VSCode Remote ContainersでGitにSSH接続でpushする (Windows)

CreateReactApp

今回の環境はReactでTypeScriptを使用し、storybookの環境構築を済ませた環境になっております。
自分で環境構築を行う場合、以下の手順でできます。

  1. Nodeを使用可能なコンテナを起動
    1. Docker Hub - Node
  2. コンテナ内で以下のコマンドを実行

    npm install -g create-react-app
    ## TypeScriptでReactのApplication構築を行う(テスト環境も配備)
    create-react-app sample --typescript
    cd sample
    # Reactのプロジェクトフォルダ配下で実行することでstorybookの環境構築を行う
    npx -p @storybook/cli sb init --type react_scripts
    

実装した内容

実際に環境変数で使用している内容の解説をします。

Dockerfile
# ベースとするDockerImage
FROM node:13.12.0-slim
WORKDIR /usr/app
# パッケージの依存関係に関するファイルをホストからDocker内にコピー
COPY package.json yarn.lock tsconfig.json ./
# yarn install する
RUN yarn
# yarn install 後、全ファイルをコピーすることで、パッケージ関連がコピーされ、キャッシュが効くようになり高速化
COPY . .
CMD [ "yarn", "start" ]
docker-compose.yaml
    # compose fileのフォーマットバージョン(バージョンによって使える記法が違う)
    version: '3.7'
    services:
        node:
            build:
            # 使用するDockerFileのDirectoryを指定
            context: ../../
            # 使用するDockerFileの名前を指定(Dockerfile.Nodeはdocker-compose.yamlと同じディレクトリにあるが、contextで2階層上を使用すると指定している、だからわざわざdocler/.devcontainer/ という風にしてDockerfile.Nodeを指定)
            dockerfile: docker/.devcontainer/Dockerfile.Node
            volumes:
            # ローカルのディレクトリが接続(マウント)する作業ディレクトリを指定(srcフォルダをusr/app/src にマウントしている)
            - ../../src:/usr/app/src
            # 外部に対して公開するポート番号
            ports:
            - "3000:3000"
            # コンテナを起動させ続けるか管理するフラグ
            tty: true
            # 環境によってはDockerでHotReloadが効かなくなるので環境変数により有効化する
            environment:
              - CHOKIDAR_USEPOLLING=true
devcontainer.json
    {
        "name": "Existing Docker Compose (Extend)",
        "dockerComposeFile": [
            "docker-compose.yml"
        ],
        "service": "node",
        "workspaceFolder": "/usr/app/", // VSCodeのTerminalが開くディレクトリを設定
        "settings": {
            "terminal.integrated.shell.linux": null,
            "files.trimTrailingWhitespace": true, // ファイル保存時に行末の空白自動削除
            "editor.formatOnSave": true // ファイル保存時に自動フォーマットが実行される
        },
        // Remote-Container で起動した際にVSCodeの拡張機能をインストール
        "extensions": ["eamodio.gitlens","chakrounanas.turbo-console-log","visualstudioexptteam.vscodeintellicode","esbenp.prettier-vscode","dbaeumer.vscode-eslint","shardulm94.trailing-spaces","msjsdiag.debugger-for-chrome","christian-kohler.path-intellisense","usernamehw.errorlens","gizak.shortcuts","coenraads.bracket-pair-colorizer-2"],
        // Remote-Container で起動した際にgitをインストール
        "postCreateCommand": "apt-get update && apt-get install -y git",
    }

苦戦した点

  1. コンテナ内でホットリロードが効かない

    1. docker-compose内でCHOKIDAR_USEPOLLINGを設定することで解決
  2. yarn start が遅い

    1. 一番苦労した点、コンテナ内だと5分かかることもありました。(ローカルだと10秒で終わる)
    2. 原因はyarn がキャッシュされておらず一々node_modulesを作成してたから
      1. 下記の記事の内容で解決しました
        1. Dockerfile入門する
    3. Docker for Macは遅いらしいのでMacの方は注意が必要らしいです。
      1. フロントエンドの開発環境に Docker は不要(少なくともMacでは)

経緯

  1. Excelや社内Wikiなどで書かれた環境構築資料の通りにやってもエラーが出て環境構築できない
  2. 結果、環境構築だけで時間が消える
  3. プロジェクトメンバーが増える度に同じ時間が無駄になる
  4. 溜まっていく疲労と虚無感
  5. Remote-Containerなら秒で終わる!
  6. これからはコンテナが入っていれば環境構築が秒で終わる
  7. みんな幸せ

参考資料

Reactの開発環境をDockerで整えよう

Docker Composeの仕様について混乱しがちな箇所を整理した

VSCode Remote ContainersでGitにSSH接続でpushする (Windows)

Dockerfile入門する

docker-composeでreact環境構築(更新版)

Todo-App-Client

Dockerizing a React App

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