20190915のReactに関する記事は5件です。

React.js(基礎)

React.js 基本

App.js

App.js
import 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;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『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/ にアクセスすると、あなたのアプリが確認できます!

ここから先の開発フロー

ここから先は以下のように開発を進めつつ

  1. yarn startでローカルサーバーを立ち上げ
  2. ソースコードを編集しhttp://localhost:3000/で確認
  3. 作業単位ごとに GitHub にプッシュ

機能単位で以下のようにデプロイしていけば良いでしょう

  1. yarn buildでアプリをビルド
  2. 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 initbuild/index.htmlを上書きしたことが原因かもしれません
    • firebase init -> yarn build -> firebase deployの順番を守ればこれは起こりません

この他にも、つまづきポイントあったら追加していきたいので、コメントくださいmm

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

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.yml
name: 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: ./out

Project 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.js
module.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/

参考

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

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のエラーであったとしても一緒に検索してみることが大事だなと思いました。

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

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を書くことにしました

セットアップ

/ # ルートディレクトリ
 └ pages
    └ api
      └ # api用の.jsファイルを置く

/pages/apiに置いたAPIは、/api/*にアクセスすることで叩くことができます
例: /pages/api/dictionary.jslocalhost:3000/dictionaryにアクセスするとGETやPOSTができます

コード

下のサンプルコードでは、
urlで設定したサイトにアクセス
→ cheerioでh1タグをスクレイピング
→ h1タグのテキストをレスポンスで返す
ということをしています

my-api.js
import 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/2be73956b788c745048f

json-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/textlocalhost: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のパラメータにするとエラーになります
そこで、文字列を処理する必要があります

処理の流れ

  1. 単語がクリックされる → être
  2. 単語の文字列を取得 → être
  3. アクサン付きの文字をエンコーディング -> %EAtre
  4. URLにアクセス -> https://www.collinsdictionary.com/dictionary/french-english/%EAtre

コード

アクサンのエンコードをする関数は以下の通りです
何も考えずにアクサン文字を1つずつString.replace()で置換します

文字列の処理は、アクサン以外にもユーザが入力した文章をデータベースに保存するとき、'&をエスケープすることも行ったため、
関数をutils/strUtils.tsxの中にまとめて置きました
アプリ本体で使うときは import { encodeAccents } from ../utils/strUtils のようインポートします

utils/strUtils.tsx
export 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をネストする方法

などがあります

機能を実装していくにあたり、細かい点を決めるとき(単語リスト画面に単語の意味を表示するかどうかなど)にどうするべきかの判断基準がぶれていたので、次の開発では最初に想定ユーザ・想定シーンをより具体的に定めていきます
データベースも、とりあえずライブラリを入れてからデータ構造を設計しましたが、こちらはライブラリをある程度使わないと仕様がわからないことと、データ構造が比較的単純だったことがあり、今回のやり方でもあまり困りませんでした

今後の予定としては、しばらく自分でアプリを使ってみて、バグ取りや更にあったら便利な機能を実装していきます

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