- 投稿日:2019-09-15T21:28:29+09:00
React.js(基礎)
React.js 基本
App.js
App.jsimport React from 'react'; class App extends React.Component { render(){ {/* script書ける(render内でreturn以外) */} const name = "タロウ忍者" return( {/* JSXの書き方例 */} {/* divで囲む */} <div> <h1>Hello World</h1> {/* img の後ろに "/"つける */} <img src="test.png" /> <p>{name}</p> {/* js変数の"タロウ忍者が表示される" */} </div> ) } } export default App;
- 投稿日:2019-09-15T17:29:21+09:00
『5秒』でTypeScript + React アプリを作り『5分』で全世界に公開する
e-Sports専用のクラウドファウンディング・サービス「e-Sports-Funding(仮)」を個人開発したいので、開発環境を整えていきます。技術的には以下を使用する予定で、各技術の導入に際し備忘録もかねて記事にしていきます。
- TypeScript : 言語
- React : UI フレームワーク
- Redux : 状態管理
- Firebase : バックエンド
- Firebase Hosting : インフラ
- Stripe : 決済プラットフォーム
なお、太字部分は本記事で導入します。
TypeScript + React アプリを作成する
- create-react-appを使ってアプリのひな型を作成
yarn create react-app e-funding --typescript # サンプルプロジェクトを作成しますここまでで『5秒』
Firebase hosting でデプロイする
- firebase プロジェクトの作成
firebase の初期設定
yarn add firebase-tools # firebase まわりをアプリに追加します firebase login # firebase が連携するgoogleアカウントを選択します firebase init # firebase の動作に必要なファイルを対話式で自動生成します対話への応答は以下
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. # Hosting: Configure and deploy Firebase Hosting sitesを選択 ? What do you want to use as your public directory? # buildと入力 ? Configure as a single-page app (rewrite all urls to /index.html)?` # nと入力
- firebase Hosting でデプロイ
yarn build # アプリを公開可能な形に変換します firebase deploy # firebase hosting でデプロイしますここまでで『5分』
アプリが公開されました?
ブラウザでhttps://[YOUR_FIREBASE_PROJECT_NAME].firebaseapp.com/
にアクセスすると、あなたのアプリが確認できます!
ここから先の開発フロー
ここから先は以下のように開発を進めつつ
yarn start
でローカルサーバーを立ち上げ- ソースコードを編集し
http://localhost:3000/
で確認- 作業単位ごとに GitHub にプッシュ
機能単位で以下のようにデプロイしていけば良いでしょう
yarn build
でアプリをビルドfirebase deploy
でアプリをデプロイ将来的にはmasterへのマージのタイミングで、GitHub からFirebase Hosting に自動的にデプロイできるようにしたいですね(いわゆるCD)
Tips
firebase
コマンドを打ち込むとcommand not found
になる
- パスが通っていないことが原因かもしれません
vim ~/.bashrc
で以下を追記してくださいexport PATH="XXXXX:$PATH"
- xxxxxは
yarn bin
で出てくるパスですちゃんとデプロイしたのに、URLにアクセスすると
Firebase Hosting Setup Complete
とかいう変な画面が出てくる
firebase init
でbuild/index.html
を上書きしたことが原因かもしれませんfirebase init
->yarn build
->firebase deploy
の順番を守ればこれは起こりませんこの他にも、つまづきポイントあったら追加していきたいので、コメントくださいmm
- 投稿日:2019-09-15T08:50:13+09:00
React Next アプリを GitHub Actions で GitHub Pages にデプロイ
React.js で作成したアプリケーション、とりわけ今回は Next.js で始めたプロジェクトを例に GitHub Actions を使って GitHub Pages へのデプロイを自動化する方法を紹介します。
Vue, Nuxt についての記事もあります。
Create Next App
上記を参考に Next のプロジェクトを作成してください。この記事作成時の Next バージョンは 9.0.5 です。
GitHub Actions の設定
peaceiris/actions-gh-pages: GitHub Actions for deploying to GitHub Pages with Static Site Generators を利用します。
GitHub Actions を用いた GitHub Pages へのデプロイ自動化方法についてより詳しく知りたい方は上記記事を参考にしてください。
ACTIONS_DEPLOY_KEY
の設定方法もこちらの記事で解説しています。
ACTIONS_DEPLOY_KEY
を設定した後、以下のファイルを master ブランチに Push してください。自動で GitHub Actions による Next のビルドと GitHub Pages へのデプロイが始まります。名称が
username/username.github.io
の repository であればhttps://username.github.io/
形式の URL で Web app が公開されます。.github/workflows/gh-pages.ymlname: github pages on: push: branches: - master jobs: build-deploy: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@master - name: setup node uses: actions/setup-node@v1 with: node-version: '10.16' - name: install run: yarn install - name: build run: yarn build - name: export run: yarn export - name: add nojekyll run: touch ./out/.nojekyll - name: deploy uses: peaceiris/actions-gh-pages@v2.3.0 env: ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }} PUBLISH_BRANCH: gh-pages PUBLISH_DIR: ./outProject pages
User and Organization リポジトリ (
username/username.github.io
) の場合は GitHub Actions の workflow を追加するだけで十分です。一方で Project pages (username/project_name
形式のリポジトリ) の場合はnext.config.js
に対して以下のような修正が必要です。Web app は
https://username.github.io/project_name/
形式の URL で公開されます。next.config.jsmodule.exports = { // some configuration assetPrefix: process.env.NODE_ENV === "production" ? "/project_name" : "" // another configuration };package.json"scripts": { "build": "env NODE_ENV='production' next build" }独自ドメイン
GitHub Pages へデプロイした React, Next アプリケーションを独自ドメインで公開する手順です。
User and Organization リポジトリで公開するために
next.config.js
を修正していた場合は取り消しておきます。以下の記事でも独自ドメインの設定方法を解説しているので参考にしてください。
CNAME
を./out
に加える step を export step の後に追加します。- name: add CNAME run: cp ./path/to/CNAME ./out/参考
- 投稿日:2019-09-15T08:24:40+09:00
Reactでreact-router-domを使おうとするとページのロードが終わらない問題を解決する
※この記事は、プログラミング歴五ヶ月程度の初心者によって書かれています。ベテランの方は大目に見てもらえると助かります。同じように初心者の人は、私の言っていることを鵜呑みにしすぎず「?」と感じた点はすぐに検索するようにしてください。
今回抜け出せなくなったこと
バックエンドはRails、フロントエンドはReactにTypeScriptを使って簡単なアプリを作ろうとしていました。
その時に、フロントエンド側でルーティング設定をしようと思い
$ npm install --save @types/react-router-domをターミナルで実行しました(セーブフラッグは無くても大丈夫だと思います。@typesというのはTypescript向けに型が付いたものなので、普通のJSでReact開発をしている人は必要ありません)。
そして、普通にコードで必要なものをインポートして使っていたのですが
Module not found: Can't resolve 'react-router-dom' in
というエラーに出くわします。
エラー文をそのまま検索したところ、TypeScript用だけじゃ無くて普通のreact-router-domもインストールした方が良いとのことだったので下のコマンドを実行しました。
$ npm install --save react-router-domそして、普通にコードを書き進め、npm startをしてlocalhostにアクセスすると画面のロードが終わらなくなりました。真っ白い画面で、コンソールにエラー表示もないまま、ずっとこのままです。
この問題を解決するのに、10時間以上のリサーチが必要になりました。
環境
内容的にバージョンは関係ないので、JSONファイルを貼っておきます。
{ "name": "react_front_end", "version": "0.1.0", "private": true, "dependencies": { "@types/jest": "24.0.18", "@types/node": "12.7.5", "@types/react-router-dom": "^4.3.1", "axios": "^0.19.0", "react": "^16.9.0", "react-dom": "^16.9.0", "react-router-dom": "^4.3.1", "react-scripts": "3.1.1", "typescript": "^3.5.3" }, "scripts": { "start": "PORT=8000 react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0" } }解決策
原因は、Routeを使って、Routeを使っているコンポーネント自身を無限ループさせていたことでした。言葉だとうまく説明できないので、コードを表示します。
これが、ブラウザで表示されなかった時のコードです(長いので簡易化しています)。
import * as React from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; class App extends React.Component<{}, {}> { render() { return( <div> <Router> <Switch> <Route path="/" component={App} /> <Route path="/count" render={() => <Count name="fantastic!" />} /> <Route path="/history" component={History} /> </Switch> </Router> <h1>サインイン</h1> <form onSubmit={e=>this.handleSubmit(e)} > <input onChange={e=>this.handleEmailChange(e)} /> <input onChange={e=>this.handlePassChange(e)} /> <button type="submit">ログイン</button> </form> </div> ) }; }このコードで問題となっていたのは、Appコンポーネントの中でAppコンポーネント用のルートを設定している下のコードでした。
<Route path="/" component={App} />解決策は単純でこの一行を削除してあげるだけです。
仕組みとしては最初にAppコンポーネントを読み込むのに、その中でそれ自身であるAppコンポーネントが読み込まれていて、それを読み込もうとするとまたAppコンポーネントが出てきて読み込んで…エラーになるということだと思います。
そもそも、元からAppコンポーネントは表示されているのでわざわざ、そこから設定しなくても大丈夫ですね。
解決までの道のり
久しぶりに思いっきりハマってしまい、しかも一切エラーメッセージのないエラーだったので原因を特定するのにかなり時間がかかってしまいました。
最初は、アプリを閉じたり色々アップデートしてみたり、再起動したり、タブ閉じたりキャッシュ削除したりと色々していました。が、もちろんダメでした。
最終的に出てきた「page unresponsive」とReactを組み合わせて検索すると、ようやく解決策にたどり着けました、
エラーにハマった時は、とにかく違うアプローチを色々試してみること、そしてChromeのエラーであったとしても一緒に検索してみることが大事だなと思いました。
- 投稿日:2019-09-15T03:39:47+09:00
vocab with context アプリ開発記録
アプリの概要
- 文章を読みながら外国語の語彙を増やす
- アプリに外国語のテキストを貼り付ける → 覚えたい単語をハイライト → 単語の意味が表示される (現在は仏→英に対応)
- Github: https://github.com/jesuissuyaa/tangochou
スクリーンショット
仕様
構成図
- 画面
- データベース: 以下のデータを保存
- ユーザの入力した単語
- ユーザがハイライトした単語
- 外部サイトから取得した単語の意味
- API: 外部の辞書サイトから単語の意味をスクレイピング→アプリに返す
ライブラリ & フレームワーク
- React
- Next.js:
create-next-app
でブートストラップ (https://nextjs.org/docs#quick-start)- Typescript
- json-server: JSON形式でデータを読み書きできるデータベース
- cheerio: スクレイピング用のパッケージ; jQueryの書き方でスクレイピングできる点がわかりやすい
- request: APIで外部サイトにリクエストするために使用
- isomorphic-unfetch: json-serverでデータを読み書きするときに
fetch
を使用アクティビティ図
開発のポイント
APIを自分で書きました
経緯
最初は翻訳のライブラリかAPIを使うことを考えていましたが、
- 一つの単語に対して複数の意味を返してくれるものが見つからない
- APIキーの取得が大変
- 有料の場合もある
- 単語単位でなく文章単位の翻訳が想定されていることが多い
ということから、自分でAPIを書くことにしました
セットアップ
- 公式ドキュメンテーションを読む (https://nextjs.org/docs#api-routes)
- API用のフォルダを作る
/ # ルートディレクトリ └ pages └ api └ # api用の.jsファイルを置く
/pages/api
に置いたAPIは、/api/*
にアクセスすることで叩くことができます
例:/pages/api/dictionary.js
はlocalhost:3000/dictionary
にアクセスするとGETやPOSTができますコード
下のサンプルコードでは、
url
で設定したサイトにアクセス
→ cheerioでh1タグをスクレイピング
→ h1タグのテキストをレスポンスで返す
ということをしていますmy-api.jsimport request from 'request'; import cheerio from 'cheerio'; export default ({ query: { id } }, res) => { request(url, (err, response, body) => { if (err) { console.error(err); res.status(500); res.end('server error'); } const $ = cheerio.load(body); const foo = $('h1').text(); res.status(200).json({ message: `scraped h1 tag and got ${foo} ` }); }, ); };クエリパラメータ
APIのURLにはパラメータを設定することができます
サンプルコードの中では{ query: { id } }
となっている部分で、URLの?id=
の値を取得しています
例えばlocalhost:3000/api/my-api?id=10
にアクセスすると、APIの中でid
の値を使うことができますcheerioでスクレイピング
サンプルコードの中では、以下の2行でcheerioを使っていますconst $ = cheerio.load(body); const foo = $('h1').text();
$ = cheerio.load(body)
でレスポンスのbodyを$
に入れることで、$('.myclass')
や$('#myid')
のようにjQuery風の書き方でスクレイピングしたいサイトに要素を取得できますresponseを返す
エラーの場合は500を返しますres.status(500); res.end('server error');成功した場合は200とJSON形式のデータを返します
res.status(200).json({ message: `scraped h1 tag and got ${foo} ` });json-serverでデータベースを実装しました
こちらの記事が大変参考になりました
https://qiita.com/t12u/items/2be73956b788c745048fjson-serverの使い方
- json-serverを
npm
でアプリに追加db.json
をルートディレクトリに用意 → データを書き込む- ターミナルからjson-serverを起動:
npm run json-server
- アプリから
localhost:30001/<データベース名>
にHTTPリクエストを投げることで読み書きするjson-serverでの読み書き
- 新規作成: GET
- 編集: PUT
- 削除: DELETE
でそれぞれデータを操作できます
アプリのデータベース構成
texts + id: 1 (number) # データのID; json-serverによって自動で振られる + text: the quick brown fox jumps over the lazy dog (string) # ユーザの入力した文章 + wordlist: ['fox', 'lazy'] (string[]) # 文章の中でハイライトされた単語 vocab + id: 1 (number) + word: lazy (string) # 単語 + definitions: ['気だるい', '怠けている', 'ゆっくりとした'] (string[]) # 単語の意味の配列コード
アプリの中では
fetch
を使ってlocalhost:3001/text
やlocalhost:3001/vocab
にHTTPリクエストを投げます
下の例ではvocabデータベースのすべてのエントリーのwordの値をとってきていますexport async function getWords () { const res = await fetch('http://localhost:3001/vocab') const data = await res.json(); return (<ul>{data.map(entry => <li>entry.word</li>)}</ul>) };<!-- getWords()の返り値の一例 --> <ul> <li>lazy</li> <li>fox</li> <li>dog</li> </ul>新たに書き込みをするときは、このようなコードになります
fetch('http://localhost:3001/vocab', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ word: 'hoge', definition: ['ほげ', 'ホゲホゲ'] }), }).then(getWords);アクサンの処理が大変でした
経緯
冒頭の概要で書いたように、今回はフランス語の文章を対象にしました
フランス語の単語の意味をAPIでとってくるときは、例えば https://www.collinsdictionary.com/dictionary/french-english/vouloir のように、URLに調べたい単語を入れます
ところが、フランス語にはアクサン記号つきの文字 (e.g. î, é) が含まれるため、単語をそのままURLのパラメータにするとエラーになります
そこで、文字列を処理する必要があります処理の流れ
- 単語がクリックされる → être
- 単語の文字列を取得 → être
- アクサン付きの文字をエンコーディング -> %EAtre
- URLにアクセス -> https://www.collinsdictionary.com/dictionary/french-english/%EAtre
コード
アクサンのエンコードをする関数は以下の通りです
何も考えずにアクサン文字を1つずつString.replace()
で置換します文字列の処理は、アクサン以外にもユーザが入力した文章をデータベースに保存するとき、
'
や&
をエスケープすることも行ったため、
関数をutils/strUtils.tsx
の中にまとめて置きました
アプリ本体で使うときはimport { encodeAccents } from ../utils/strUtils
のようインポートしますutils/strUtils.tsxexport const encodeAccents: (str: string) => string = (str: string) => str .toLowerCase() .replace(/ç/g, '%E7') .replace(/é/g, '%E9') .replace(/â/g, '%E2') .replace(/ê/g, '%EA') .replace(/î/g, '%EE') .replace(/ô/g, '%F4') .replace(/û/g, '%FB') .replace(/à/g, '%E0') .replace(/è/g, '%E8') .replace(/ù/g, '%F9') .replace(/ë/g, '%EB') .replace(/ï/g, '%EF') .replace(/ü/g, '%FC');備考
文字列の処理について、アポストロフィには
'
の他にも’
が使われていることがありますまとめ
開発を通して、
- Next.jsを使ったReactアプリケーションの開発
- json-serverでのデータベース実装
- APIの書き方
- fetchの使い方
などを学びました
未解決な点としては、
- Next.jsで
.scss
ファイルを扱う方法request
をネストする方法などがあります
機能を実装していくにあたり、細かい点を決めるとき(単語リスト画面に単語の意味を表示するかどうかなど)にどうするべきかの判断基準がぶれていたので、次の開発では最初に想定ユーザ・想定シーンをより具体的に定めていきます
データベースも、とりあえずライブラリを入れてからデータ構造を設計しましたが、こちらはライブラリをある程度使わないと仕様がわからないことと、データ構造が比較的単純だったことがあり、今回のやり方でもあまり困りませんでした今後の予定としては、しばらく自分でアプリを使ってみて、バグ取りや更にあったら便利な機能を実装していきます