20190829のReactに関する記事は6件です。

初めてのvue.js 簡易的なメモ・備忘録編

はじめに

まず、私の説明だけ簡単にさせていただきます。
とあるベンチャー企業でフロントエンドエンジニアとして働いています。
ライブラリーはReactを使い、1年にも満たないですが、redux,redux-saga,非同期通信などなど、様々なことに挑戦して、Reactを自信を持って書けるようになりました。
そんな私が、あるきっかけでvue.jsを使用することになってので、勉強録としてまとめてみました。

今回は、まとめというより、メモという意味合いが強いです。
vue.jsやフロントエンドに強い方、謝りや誤認識等ありましたら、ぜひご指摘ください!

こちらの書籍をベースに学習しております。
Reactに馴染んでいる私にとって、非常にわかりやすく、スラスラ読める一冊です!
初めてプログラミングに触れた時は、全く理解できなかったが………
Vue.js入門 基礎から実践アプリケーション開発まで

それでは。

オプション(メソッドのようなもの)

data

UIの状態・データ
Reactでいうstateみたいなものかな?変数のような概念かな?
この data を通して、html側にデータを送るようです。
名前から役割が想像しやすいですね。

el

vueインスタンスをマウントする要素。
マウントがよく分かっていません。
elでマウントすることで、該当htmlにデータを送受信したり、計算したり、加工したりできるようになる。

メソッドによるマウントもでき、その場合は$mountを使うようです。

filters

データを文字列と整形する。
データを 計算 するのではなく、あくまでも整形する。
後述ののcomputedと役割が被って見えます。

methods

イベントが発生した時などの振る舞い。
onClick等の処理を書いているところですね。

computed

データから派生して算出される値。
要は、受け取ったデータを 計算 しているものですね。
前述のfiltersと役割が被って見えます。

テンプレート構文

{{}} Mustache構文(二重中括弧)という名前らしいです。

ディレクティブ

オプションと同じように、vue.jsで重要な概念の一つ。
ディレクティブにより、onClickで処理をメソッドに渡したり、繰り返しの構文を使えたり、formのデータを取得できるようです。

v-bind

`v-bind: 属性名 = "データを展開した属性値"
buttonのdisableやstyleなどに、vueのデータやメソッドを挟み込めるようになりますね。

省略記法として、: + 属性名で記述できます。

<p v-bind:class="hoge"><p :class="hoge">

v-if/v-show

条件付きレンダリング。
名前だけやと分からないですね。
非表示・非表示を切り替える場合に使うものです。

見た目上は同じような動きをしますが、v-ifはDOMの追加・削除を、v-showはdisplayプロパティの変更をするようです。

v-for

リストレンダリング。
jsのfor構文と同じく、html上でデータの繰り返し処理ができるようです。

v-on

イベントハンドリング。
onClickやonChange属性にメソッドや処理を渡せるようです。

省略記法として@があります。

<button v-on:click="hoge"><button @click="hoge">

v-model

フォーム入力バインディング。
双方向バインディングを実現できる。
Reactでは少し記述が冗長になるため、これは非常に便利!

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

vte.cxによるバックエンドを不要にする開発(その5)

前回=>vte.cxによるバックエンドを不要にする開発(その4)

今回はデータ登録における一貫性について、ホテル予約サンプルアプリケーションを元に説明します。

データ一貫性を考慮したホテル予約アプリケーションを作る

まず、ホテルを予約するシンプルなアプリケーションを作ってみましょう。

ここで大事になるのは、複数の顧客が、同じ部屋を予約しようとした場合の処理です。当然ですが各部屋は顧客一人しか予約できません。絶対に複数の顧客が同じ部屋を予約できないようにしなくてはいけません。

例えば 101 の部屋の空き状況を、二人の顧客(A と B)が同じタイミングで確認したとしましょう。そのときには 101 は空いていました。その後、A は B よりも早く 101 を予約します。その少し後に、B も 101 を予約しようとします。なぜなら B が見ている空き状況は、最初に確認したタイミングのものですから、A が予約したことが反映されていないからです。このような状況で予約ボタンを押した場合、「既に予約されているのでエラーにする」必要があります。

こういったシステムを作るために vte.cxには、idを元にした楽観的排他という仕組みがあります。
それぞれのentryには一意の識別子としてidがあり、idには更新回数であるrevisionが含まれています。(revisionは更新の都度+1されることになります) この revision を含む id を書き込み時に参照することで、既に他の顧客が更新した後なのかどうなのか、ということを判別しています。

では早速、アプリケーションの以下の項目を管理画面の新規エントリ項目追加から登録しましょう。
登録方法はこれまで行っていますのでわかりますね。

room
 name
 reservation(boolean)

エントリ項目一覧で以下のように表示されればOKです。(※ 前回使用したuserなどの項目が残っていても問題ありません)

スクリーンショット 2019-08-29 17.02.02.png

また、エンドポイント管理で/hotelというエンドポイントを追加しましょう。
(※ 前回使用した/fooが残っていても問題ありません)

スクリーンショット 2019-08-29 17.00.56.png

登録したら、設定ファイルをダウンロードして更新しておきましょう。

npm run download:template
npm run download:typings

アプリケーションのソースコードは以下のようになります。

index.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { useState } from 'react'
import axios from 'axios'

const App = () => {

  const [rooms, setRooms] = useState<VtecxApp.Entry[]>([])

  const initdata: VtecxApp.Entry[] = [
    {
      room: {
        name: '101',
        reserved: false
      },
      link: [
          {
              "___href": "/hotel/101",
              "___rel": "self"
          }
      ]
   },
   {
    room: {
      name: '102',
      reserved: false
    },
    link: [
        {
            "___href": "/hotel/102",
            "___rel": "self"
        }
    ]
   }
  ]

  const putrooms = async (req:VtecxApp.Entry[]) => {
    try {
      axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
      await axios.put('/d/hotel',req)
      getrooms()
    } catch (e) {
      alert('error:'+e)
    }
  }

  const getrooms = async () => {
    try {
      axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
      const res = await axios.get('/d/hotel?f')
      setRooms(res.data)
    } catch (e) {
      alert('error')
      console.log(e)
    }
  }

  const showrooms = () => {
    return rooms.map((entry,index) => {
      if (entry.room&&!entry.room.reserved) {
      return (
                <div key={index}>
                    <p>{entry.room.name} 
                    <button
                        onClick={() => {
                            reserve(entry)
                        }}
                    >
                        予約
                    </button>
          </p>
                </div>
      )
    }
  })
}

const reserve = async (entry:VtecxApp.Entry) =>{
  if (entry.room) {
    entry.room.reserved = true
    putrooms([entry])
  }
}

  return (
    <div>
      <button onClick={() => { putrooms(initdata) }}>
       初期化
      </button>
      <button onClick={() => { getrooms() }}>
       一覧
      </button>
      <br/>
      {showrooms()}
    </div>
  )

}

ReactDOM.render(<App/>, document.getElementById('container'))

これを、npm run serve:indexで起動してください(未ログインの場合は、先にnpm run serve:loginを実行してください)

すると、以下のような画面が出ますので、初期化ボタンを押してください。
ホテルの部屋一覧が表示されると思います。

スクリーンショット 2019-08-29 17.30.48.png

このとき、サーバに登録されたデータは以下のようになっています。

スクリーンショット 2019-08-29 17.49.24.png

部屋番号の右の予約ボタンを押すと予約が実行されてリストから消えます。
101号室と102号室をそれぞれ予約してみてください。元に戻すには初期化ボタンを押します。

楽観的排他制御の確認

次に、ブラウザのタブをもう一つ開いてアプリケーションを表示させてください。
1つ目のアプリケーション画面では初期化ボタンを押し、別のアプリケーション画面では一覧ボタンを押し、101号室を予約してください。そして、1つ目のアプリケーションに戻り、まだ表示されている101号室の予約を実行してください。

このとき、2つ目のアプリケーションが先に101号室を予約しているので、1つ目のアプリケーションはエラーになるはずです。実際に予約を実行すると以下のようなエラー画面が出てくると思います。

スクリーンショット 2019-08-29 17.30.48.png

デベロッパーツールで詳しくエラーメッセージを見ると、{"feed" : {"title" : "Optimistic locking failed. Key = /hotel/101"}のように出ています。
これは楽観的排他エラーが発生したことを意味します。

つまり、1つ目のアプリケーションで更新しようとしたentryのidが、既に2つ目のアプリケーションにより更新されて+1されており、一致しなかったというわけです。楽観的排他エラーになることで、ダブルブッキングをさせない空室予約が可能になるのです。

ちなみに、強制的に上書きする方法もあります。それは、PUT更新時にid項目を含めないでリクエストすることで、楽観的排他エラーを敢えて発生させずに更新することができます。

今回はここまでです。お疲れ様でした。

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

静的サイトジェネレータReact-Staticを使ってみる

はじめに

プログラミング歴2ヶ月の素人が書いています。(間違っていたらご指摘ください)

「React-Staticをはじめて使ってみる」ための学習メモです。

  • React-Staticプロジェクトの生成

  • Netlifyへのデプロイ

上記2点についてまとめます。

?React-Static公式?
https://github.com/react-static/react-static

今回の目的

静的サイトジェネレータReact-Staticを初めて使います。

簡単に爆速表示のブログサイトを自動生成して、Netlifyにデプロイします。

Node.jsのインストール

ReactはNode.jsを必要としますので、インストールします。
https://nodejs.org/ja/

yarnのインストール

https://yarnpkg.com/lang/ja/

パッケージマネジャーとしては、Node.jsに付いてくる標準のnpmもありますが、私の環境と実力ではうまくビルドができませんでした。

npmからyarnに変えたところうまくいったので、ここはyarnを使うのがいいと思います。

React-Staticのインストール

$ yarn global add react-static

グローバルにReact-Staticをインストールします。

React-Staticプロジェクトの作成

$ cd プロジェクトのディレクトリ
$ react-static create

プロジェクト名と、テンプレート名を聞かれます。
テーンプレートは、『Basic』を選択しました。

ローカルホストで確認

localhost:3000から作成したプロジェクトにアクセスできます。

本番環境のビルド

$ yarn build

本番用のファイルとして、distファイルが生成されます。

githubにプッシュ

まずはgithubにプッシュして、そのリポジトリをNetlifと連携することでデプロイします。

distだけでなく、プロジェクト全体をプッシュします。

Netlifyにデプロイ

https://www.netlify.com/

SignUpを済ませます。

今回はgithubと連携させてデプロイします。

Screen Shot 2019-08-29 at 16.25.06.png

連携の際、

  • 『Build Command』 => yarn build

  • 『Publish Directory』 => dist

を入力します。

これによって、今後githubに変更がプッシュされるたびに、Netlifyが自動でyarn buildを実行し、生成されるdistファイルをデプロイしてくれます。

私たちはgithubにプッシュするだけでいいので、手間が省けます。

まとめ

  • React-Staticプロジェクトの生成

  • Netlifyへのデプロイ

上記2点についてまとめました。

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

ReactでQiitaAPIを叩いてアプリを作る入門

はじめに

Reactの勉強を始めたので手始めにAPIを叩いて超簡単なアプリを一から作ってみる練習をしました。
今回使用したAPIはQiita API v2です。作成したアプリはGitHubのリポジトリにあげてあります。

どのAPIを叩くか決めるとき参考にした記事
https://qiita.com/mikan3rd/items/ba4737023f08bb2ca161
アプリを作るときに参考にした記事
https://qiita.com/gcyagyu/items/4d186df2e90c53228951

create-react-appでプロジェクトの作成

create-react-appを使うことでアプリの雛形を作ることができます。今回はreact-api-studyというアプリ名で作成しています。

参考
https://create-react-app.dev/

$ create-react-app react-api-study

axiosのインストール

アプリを作成したらディレクトリを移動して、axiosをインストールします。

$ cd react-api-study
$ yarn add axios

アプリの実装

今回はQiitaAPIのhttps://qiita.com/api/v2/itemsのエンドポイントを叩いて記事一覧を取ってきて、タイトルとURLを表示させます。

GET /api/v2/items
記事の一覧を作成日時の降順で返します。

  • page
    • ページ番号 (1から100まで)
    • Example: 1
    • Type: string
    • Pattern: /^[0-9]+$/
  • per_page
    • 1ページあたりに含まれる要素数 (1から100まで)
    • Example: 20
    • Type: string
    • Pattern: /^[0-9]+$/
  • query
    • 検索クエリ
    • Example: "qiita user:yaotti"
    • Type: string

src/index.jsの拡張子を変えてsrc/index.jsxを記述します

src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

// 次に記述する src/components/App.jsx をインポートする
import App from './components/App';

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

srcディレクトリの下にconponentsディレクトリを作成し、その中にApp.jsxファイルを作成し次のソースを記述します。

src/conponents/App.jsx
import React, { Component } from 'react';
// axiosをインポート
import axios from 'axios';

class App extends Component {
  constructor(props) {
    super(props);
    this.getQiitaPosts = this.getQiitaPosts.bind(this);
    this.state = {
      // ここを`React`など検索したいワードに変えられる
      query: 'React'
    }
  }

  // QiitaAPIを叩く
  getQiitaPosts() {
    //axios.get(APIのエンドポイント,パラメータの引数)
    axios.get('https://qiita.com/api/v2/items', {
        params: {
          "page": "1",
          "per_page": "20",
          "query": this.state.query,
        }
      })
      // response にAPIからのレスポンスが格納される
      .then((response) => {
        // data にレスポンスから帰ってきた1つ目の記事の情報を格納
        const data = response.data[0];
        this.setState({
          title: data.title,
          url: data.url,
        });
        // コンソールから response と title と url を確認
        console.debug(response, "ressponse");
        console.debug(this.state.title, "title")
        console.debug(this.state.url, "url")
      })
      .catch((error) => {
        console.debug(error);
      });
  }

  // 表示されるHTMLを記述
  render() {
    return (
      <div className="App">
        <h1 className="app-title">Hello Qiita API</h1>
        <p>タイトル: {this.state.title}</p>
        <p>URL: <a target="__blank" href={this.state.url}>{this.state.url}</a></p>
        <input
          type="button"
          value="検索"
          onClick={() => this.getQiitaPosts()}
        />
      </div>
    )
  }
}

export default App;

{this.state.title}などでAPIから取ってきた値を埋め込んで表示させています。
<a target="__brank" href={this.state.url}>aタグのhref属性にurlを指定してリンクを埋め込んでいます。
onClick={() => this.getQiitaPosts()}で検索ボタンをクリックしたときにQiitaAPIを叩く関数を呼び出しています。

完成

ターミナルでnpm startで起動してlocalhost:3000を開きます

検索ボタンを押したあとの画面
スクリーンショット 2019-08-29 12.19.04.png

おわりに

間違っていることや改善したほうが良いことがあればコメントで優しく教えて下さいmm

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

Reactでよく使われるライブラリをオールインワンにしたボイラーテンプレートを作った

React+Typescriptを使ったシステム構築を検討する上で、「これは使うよなー」と思ったライブラリをまとめて、サンプル付きのボイラーテンプレートを作りました。

動機

Reactで実際に動くシステムを作ろうすると、色々なライブラリを使いたくなる。
でも、一つのライブラリを動かすために、ディレクトリ構成を考え直したり設定をコードに書いていくのは本当に大変。。。
なので、共通して使えそうなエッセンスだけを抽出してボイラーテンプレートを作りました!

Demo

sampleImage.gif

リポジトリの場所

https://github.com/joolen/react-project-template

使い方

下記のコマンドを実行するだけ。

# clone this repository
git clone https://github.com/joolen/react-project-template.git my-app
# change direcory you made
cd my-app
# install dependencies
npm install
# run project. you can access http://localhost:3000
npm start

使用ライブラリ

Name Note ドキュメントURL
React これがなければ始まらない。 https://reactjs.org/
Typescript 所謂、AltJS。解説は巷に溢れているが、静的な型付をすることが特徴。 https://www.typescriptlang.org/
material-ui 簡単にそれなりの画面を作る為のコンポーネント群を提供してくれる。 https://material-ui.com/
react-redux 非常にわかりやすい記事があるので添付
たぶんこれが一番分かりやすいと思います React + Redux のフロー図解
https://redux.js.org/
https://react-redux.js.org/
typescript-fsa(-reducers) TypescriptでReduxを使うときのボイラーテンプレートです。これにより、記述を簡略化出来ます。 https://github.com/aikoven/typescript-fsa
https://github.com/dphilipson/typescript-fsa-reducers
redux-saga React Redux構成でAPI通信を行う為のMiddlewareです。 https://redux-saga.js.org/
react-router SPAで画面遷移をさせる為のデファクトスタンダードライブラリ。これを使うことで、ブラウザの「戻る」ボタンなども使える様になる。 https://reacttraining.com/react-router/
Storybook 作成したコンポーネントを単体で見られる様にしてくれるツール。
(このツール自体はReactには限らない。vue.jsなどでも利用可能。)
https://storybook.js.org/

ディレクトリ構成

こんな感じ

my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── .tsconfig.json
├── .storybook     # StoryBookの設定関連ファイル
│   ├── addons.js
│   ├── config.js
│   └── webpack.config.js
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
│   ├── API        # 外部APIとの通信はここにまとめる
│   ├── components # Atomic Designを使ってコンポーネントを管理している
│   │   ├── atoms
│   │   ├── molecules
│   │   ├── organisms
│   │   ├── templates
│   │   ├── pages
│   │   └── stories # StoryBookの定義
│   └── containers  # Redux connectはここにまとめている
│   └── routes      # SPAなので、各ページへのルーティングはここに記述
│   │   └── index.tsx
│   ├── sagas        # Redux-sagaの関連処理

│   │   └── index.ts # root saga
│   ├── modules      # Reduxのmodules。変則的かもしれないど、Ducks
│   ├── tests        # JestとStoryShotsをここで利用
│   │   └── __snapshots__ # snapshot
│   │   │   └── storyshots.test.ts.snap # snapshotファイル
│   │   └── reducers      # reducersのテストコード
│   │   │   └── sample.test.ts
│   │   └── storyshots.test.ts # storyshots
│   ├── App.css
│   ├── configureStore.ts # Reduxで使うStoreのconfigurationがまとまっている
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   └── serviceWorker.ts
└─────

この構成にした理由

別途、まとめ中です。下記のような構成で書いていこうと思います。

Reactのアーキテクトを考える道のり - その1 (最小の構成)
Reactのアーキテクトを考える道のり - その2(Fluxとの出会い)
Reactのアーキテクトを考える道のり - その3(非同期処理でハマる)→Redux-saga
Reactのアーキテクトを考える道のり - その4(コンポーネントのディレクトリ構成に悩む)→Atomic Designとの出会い
Reactのアーキテクトを考える道のり - その5(テストについて)→JestやStoryshotsについて

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

Reduxを使い始めたらthisに悩まされなくなった

javascriptのthisは非常に癖が強く使う場合は注意が必要です。
最近はアロー関数のおかげである程度マシになりましたが、
逆に旧来のjsに慣れ親しんでいる人からするとthisが切り替わらないことが気持ち悪かったりもするようで、厄介なことに変わりありません。

javascriptは後方互換性を大切にする言語なので、thisレベルの大きな言語仕様が変更されることはおそらくないでしょうし、
typescriptを使ってもjavascriptのスーパーセットである以上、thisはthisのままです。
これはいつまで経ってもフロントではthisに悩まされ続けるのかなぁとうんざりしていたのですが、
ふと気づくと最近thisに悩まされないどころか、書いてすらいないことに気づきました。

何がきっかけなんだっけ?と振り返ってみたところ、どうやらReduxのおかげのようでした。

Reduxを使うと処理と状態が分離する

thisが必要になるのは、いわゆるオブジェクト指向的な、
状態を持ったオブジェクトに紐づく関数を使う場合です。

しかし、Reduxを使うとStateはStoreに全て集約されます。
Reducerは純粋な関数ですし、ActionCreatorも引数に応じてActionをDispatchするだけ、
ContainerはStateからPropsを生成してPresentationalComponentに渡すだけ、
PresentationalComponentは当然FunctionalComponentなのでStateは持ちません。

つまり、thisを使う必要のあるシーンがありません。

まとめ

  • thisはStateとロジックが結合している場合に必要になる
  • reduxはStateを一か所に集約するためthisが必要なくなる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む