20210302のReactに関する記事は14件です。

pm2でnext js を永続化させる方法について。プロダクション環境での注意点も含めてご紹介

next jsを永続化し落ちないようにするために試行錯誤したのでご紹介。
next jsをAWS ec2 のような環境に直接デプロイ(npm run start)すると安定せず突然サイトが落ちることがあります。
pm2を使用すると自動的にサーバーを再起動&監視してくれプロダクション環境でも安定して使用可能です。

Step1

pm2 を環境にインストール

npm install -g pm2

上記のコマンドをPC(もしくはクラウド環境)に打ち込むとインストールできます。

Step2

next jsをビルドする。

npm run build

ビルドすることにより、高速にページが開けるようになります。

Step3

pm2でnext jsを永続化

pm2 start npm --name "next" -- start

ちなみに以下のパスから起動すると、ビルド前の非効率なサーバーが永続化されるので要注意。
ページスピードインサイトで構築中のサイトを”startで起動したもの”と"以下のパスで起動したも"の比較すると、ページの送信サイズが10倍ほど違うという結果になりました。(当然重すぎてまともな時間で動きませんでした。)

./node_modules/next/dist/bin/next

Step4

pm2でサーバーが永続化されていることを確認

pm2 list

status がonlineになっていれば完了です。
スクリーンショット 2021-03-02 22.20.45.png

以上で完了!

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

pm2でnext js を永続化させる方法について。表示速度向上で試行錯誤したのでメモ

next jsを永続化し落ちないようにするために試行錯誤したのでご紹介。
next jsをAWS ec2 のような環境に直接デプロイ(npm run start)すると安定せず突然サイトが落ちることがあります。
pm2を使用すると自動的にサーバーを再起動&監視してくれプロダクション環境でも安定して使用可能です。

Step1

pm2 を環境にインストール

npm install -g pm2

上記のコマンドをPC(もしくはクラウド環境)に打ち込むとインストールできます。

Step2

next jsをビルドする。

npm run build

ビルドすることにより、高速にページが開けるようになります。

Step3

pm2でnext jsを永続化

pm2 start npm --name "next" -- start

ちなみに以下のパスから起動すると、ビルド前の非効率なサーバーが永続化されるので要注意。(googleで検索したらよく出てくるやつ)
ページスピードインサイトで構築中のサイトを”startで起動したもの”と"以下のパスで起動したも"の比較すると、ページの送信サイズが10倍ほど違うという結果になりました。(当然重すぎてまともな時間で動きませんでした。)
startだとかなり早くなります。

./node_modules/next/dist/bin/next

Step4

pm2でサーバーが永続化されていることを確認

pm2 list

status がonlineになっていれば完了です。
スクリーンショット 2021-03-02 22.20.45.png

以上で完了!

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

TypeScriptをなんとなく理解する③ ~型を組み合わせる: Generics~

この記事は何?

TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第3弾です!

今回は、型の使いまわしであるGenericsについて見ていきます。

過去の記事、続編も合わせて御覧ください!
JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
TypeScriptをなんとなく理解する② ~型を組み合わせる: Union~
・ TypeScriptをなんとなく理解する③ ~型を組み合わせる: Generics~ ← この記事
・ TypeScriptをなんとなく理解する④ ~構造型~ (3/3更新予定)

型の組み合わせとは?

TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。

この記事では、Genericsの概要について説明していきます。

※ Unionについては、前回の記事で紹介しています。

Genericsとは?

TypeScriptにおけるGenerics(ジェネリクス)を学ぶ前に、そもそもGenericsの英単語の意味を確認してみます。

genericsとは
一般的な、ジェネリックの、属の、ノーブランド商品

なんだか、定まっていないというか、汎用的な意味合いを感じます。

実際に、TypeScriptにおける、Genericsも同じような意味です。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。

事前にガッチガチに型定義するのもいいですが、汎用的に使える型もほしいです。
そこで、Genericsが活躍します。

もう少しイメージを掴むために、実際にコードを見ていきます。

まず、今までの知識を使って、関数を作ってみます。

const returnStrArray = (value1: string, value2: string):string[] => {
    return [value1, value2];
};

const returnNumberArray = (value1: number, value2: number):number[] => {
    return [value1, value2];
};

const penguinsName = returnStrArray('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン']
const penguinsWeight = returnNumberArray(1000, 50); // [1000, 50]

上のコードは、引数value1, value2を含む配列を返す関数です。
...同じようなことを2回もやるのって冗長じゃないですか...?

そこで、汎用的なことを出来るGenericsを利用します?

const returnArray = <T>(value1: T, value2: T):T[] => {
  return [value1, value2];
}

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン']
const penguinsWeights = returnArray<number>(1000, 50); // [1000, 50]

Tは何にでも変わる型です。

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン');

<string>のように指定すると、Tstringであることを示します。なので、以下の型を定義した関数と同義になります。

const returnArray = (value1: string, value2: string):string[] => {
  return [value1, value2];
}

アロー関数でGenericsを利用するときは、<T>()の前につけてあげる必要があるため<T>()のような形になっています。

このように、Genericsを利用することで汎用的で使いまわし可能な型定義をすることが出来ます。

TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。

この伏線を回収することが出来たと思います。
T<string>のように指定するまで型が確定しないことから、

今回の例では、利用するまで型が確定しない関数を実現出来たと思います。

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // 利用したから型が確定

他にも、

interface funcs<T> {
  add: (obj: T) => void;
  get: () => T;
}

みたいに、独自の型を宣言出来たりもします。

おわりに

できるだけわかりやすく伝えようとしましたが、理解出来そうでしょうか?
この記事だけで理解していただけましたら、是非LGTM✨いただければと思います。

理解できない方がいた場合は...もっとわかりやすく伝えられるよう精進します...!

参考文献

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

TypeScriptをなんとなく理解するための記事③ ~型を組み合わせる: Generics~

この記事は何?

TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第3弾です!

今回は、型の使いまわしであるGenericsについて見ていきます。

過去の記事、続編も合わせて御覧ください!
JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
・ TypeScriptをなんとなく理解するための記事③ ~型を組み合わせる: Generics~ ← この記事
・ TypeScriptをなんとなく理解するための記事④ ~構造型~ (3/3更新予定)

型の組み合わせとは?

TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。

この記事では、Genericsの概要について説明していきます。

※ Unionについては、前回の記事で紹介しています。

Genericsとは?

TypeScriptにおけるGenerics(ジェネリクス)を学ぶ前に、そもそもGenericsの英単語の意味を確認してみます。

genericsとは
一般的な、ジェネリックの、属の、ノーブランド商品

なんだか、定まっていないというか、汎用的な意味合いを感じます。

実際に、TypeScriptにおける、Genericsも同じような意味です。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。

事前にガッチガチに型定義するのもいいですが、汎用的に使える型もほしいです。
そこで、Genericsが活躍します。

もう少しイメージを掴むために、実際にコードを見ていきます。

まず、今までの知識を使って、関数を作ってみます。

// 引数value1, value2は文字列型で、返り値は各要素が文字列の配列
const returnStrArray = (value1: string, value2: string):string[] => {
    return [value1, value2];
};
// 引数value1, value2は数値型で、返り値は各要素が数値の配列
const returnNumberArray = (value1: number, value2: number):number[] => {
    return [value1, value2];
};

const penguinsName = returnStrArray('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン']
const penguinsWeight = returnNumberArray(1000, 50); // [1000, 50]

上のコードは、引数value1, value2を含む配列を返す関数です。
...同じようなことを2回もやるのって冗長じゃないですか...?

そこで、汎用的なことを出来るGenericsを利用します?

const returnArray = <T>(value1: T, value2: T):T[] => {
  return [value1, value2];
}

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン']
const penguinsWeights = returnArray<number>(1000, 50); // [1000, 50]

Tは何にでも変わる型です。

// T → string
const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン');

<string>のように指定すると、Tstringであることを示します。なので、以下の型を定義した関数と同義になります。

const returnArray = (value1: string, value2: string):string[] => {
  return [value1, value2];
}

アロー関数でGenericsを利用するときは、<T>()の前につけてあげる必要があるため<T>()のような形になっています。

このように、Genericsを利用することで汎用的で使いまわし可能な型定義をすることが出来ます。

TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。

この伏線を回収することが出来たと思います。
T<string>のように指定するまで型が確定しないことから、

今回の例では、利用するまで型が確定しない関数を実現出来たと思います。

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // 利用したから型が確定

他にも、

interface funcs<T> {
  add: (obj: T) => void;
  get: () => T;
}

みたいに、独自の型を宣言出来たりもします。

おわりに

できるだけわかりやすく伝えようとしましたが、理解出来そうでしょうか?
この記事だけで理解していただけましたら、是非LGTM✨いただければと思います。

理解できない方がいた場合は...もっとわかりやすく伝えられるよう精進します...!

参考文献

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

TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~

この記事は何?

TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第3弾です!

今回は、型の使いまわしであるGenericsについて見ていきます。

@ 過去の記事、続編?も合わせて御覧ください!
 1. JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
 2. TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~
 3. TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~ ← この記事
 4. TypeScriptをなんとなく理解するための記事④ ~構造型~
@ TypeScriptがなんとなく理解できたら、是非インストールして、触ってみてください!
 ? 【画像で説明】シンプルにTypeScriptを導入して使う方法

型の組み合わせとは?

TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。

この記事では、Genericsの概要について説明していきます。

※ Unionについては、前回の記事で紹介しています。

Genericsとは?

TypeScriptにおけるGenerics(ジェネリクス)を学ぶ前に、そもそもGenericsの英単語の意味を確認してみます。

genericsとは
一般的な、ジェネリックの、属の、ノーブランド商品

なんだか、定まっていないというか、汎用的な意味合いを感じます。

実際に、TypeScriptにおける、Genericsも同じような意味です。
TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。

事前にガッチガチに型定義するのもいいですが、汎用的に使える型もほしいです。
そこで、Genericsが活躍します。

もう少しイメージを掴むために、実際にコードを見ていきます。

まず、今までの知識を使って、関数を作ってみます。

// 引数value1, value2は文字列型で、返り値は各要素が文字列の配列
const returnStrArray = (value1: string, value2: string):string[] => {
    return [value1, value2];
};
// 引数value1, value2は数値型で、返り値は各要素が数値の配列
const returnNumberArray = (value1: number, value2: number):number[] => {
    return [value1, value2];
};

const penguinsName = returnStrArray('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン']
const penguinsWeight = returnNumberArray(1000, 50); // [1000, 50]

上のコードは、引数value1, value2を含む配列を返す関数です。
...同じようなことを2回もやるのって冗長じゃないですか...?

そこで、汎用的なことを出来るGenericsを利用します?

const returnArray = <T>(value1: T, value2: T):T[] => {
  return [value1, value2];
}

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // ['コウテイペンギン', 'アデリーペンギン']
const penguinsWeights = returnArray<number>(1000, 50); // [1000, 50]

Tは何にでも変わる型です。

// T → string
const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン');

<string>のように指定すると、Tstringであることを示します。なので、以下の型を定義した関数と同義になります。

const returnArray = (value1: string, value2: string):string[] => {
  return [value1, value2];
}

アロー関数でGenericsを利用するときは、<T>()の前につけてあげる必要があるため<T>()のような形になっています。

このように、Genericsを利用することで汎用的で使いまわし可能な型定義をすることが出来ます。

TypeScriptにおけるGenericsは、利用されるまで型が確定しない、クラス, 関数, インターフェイスなどなど...を実現するためのものです。

この伏線を回収することが出来たと思います。
T<string>のように指定するまで型が確定しないことから、

今回の例では、利用するまで型が確定しない関数を実現出来たと思います。

const penguinsNames = returnArray<string>('コウテイペンギン','アデリーペンギン'); // 利用したから型が確定

他にも、

interface funcs<T> {
  add: (obj: T) => void;
  get: () => T;
}

みたいに、独自の型を宣言出来たりもします。

おわりに

できるだけわかりやすく伝えようとしましたが、理解出来そうでしょうか?
この記事だけで理解していただけましたら、是非LGTM✨いただければと思います。

理解できない方がいた場合は...もっとわかりやすく伝えられるよう精進します...!

Next: TypeScriptをなんとなく理解するための記事④ ~構造型~

@ ハンズオンも書いたので、さっそく使ってみたい人は是非!
 ?【TypeScriptハンズオン①】男もすなるTypeScriptといふものを、女もしてみむとてするなり

参考文献

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

Reactのmaterial-tableで外部APIを利用している場合のソート実装でやったこと

目的

Reactのmaterial-tableで、dataに外部APIを利用している場合のソート実装とスタイル調整の覚書です

バージョン

下記バージョンで確認しました

  • React 17.0
  • material-table 1.69.2
  • material-ui-core 4.11.3
  • lodash 4.17.21

公式サンプル

データに外部APIを利用する方法は 公式のRemote Data Previewの通りですが、公式サンプルではソートが出来ません。
これは、変更があるたびに新しいquery objectで関数が呼ばれるのですが、呼び出しているAPI reqres.in でソートに対応していないからです。

ソートの実装方法

ソートするとデータ変更が走り queryorderByorderDirection が連携されるので、これを利用してソートを実装します。

案1 APIでソートに対応する

リクエストパラメータで指定された項目、並び順でソートしてレスポンスを返すAPIを作成し、UIから呼び出す方法です

UIからAPIに連携するパラメータ

ページングするので、ソート情報と合わせてページング情報もAPIに連携します。
query.pageSizequery.page はoptionsでページングを利用しない設定をしない場合でも存在するので必須とみなして良いでしょう。
初期表示時など query.orderByquery.sortDirection が存在しない場合はAPIのリクエストパラメータに含める必要はありません。

下記リクエストパラメータをAPIに連携すれば良いと思いますが、実際の要件によるので確認してください。

  • ページング情報:必須
    • 1ページあたりの件数 (query.pageSize )
    • 現在何ページか (query.page + 1)
  • ソート情報:任意
    • ソートする項目 (query.orderBy.field)
    • ソート順 (query.orderDirection)

APIでの対応

UI側からのリクエストパラメータを受け付ける

セキュリティ対策で、リクエストを受け付けた後はバリデーションを行いましょう。

データ検索処理

連携されたリクエストパラメータを利用して、データの検索を行います。
動的SQLでページング、ソートを行うことになるでしょう。
ソートする項目はUI上のフィールド名のため、データベースのカラム名とUI上のフィールド名の変換を持つ必要があるかもしれません。

APIレスポンスの作成と返却

公式のUsageに従い下記をレスポンスで返却します。JSONが便利です。
https://material-table.com/#/docs/features/remote-data

  • 検索結果
    • material-tableの columns で指定するフィールドが必要です
  • テーブルに表示する全レコード件数
    • ページング、ソート有無関係ない、テーブルの全件数です
    • ページングする場合、何件中何件の形で表示されるため必要です
  • 現在のページ数

案2 API返却結果をUI側でソートする

API仕様上ソート結果を反映できない場合は、lodashなどを使って result.data をソートします。

初期表示時など query.orderByquery.sortDirection が存在しない場合は、ソートせず result.data をそのままdataに設定します。

おまけ:スタイルの調整

hover,activeの色の変更

スタイル次第でこのように、項目をクリックしてソートした後に文字が見えない状態になることがあります。

スクリーンショット 2021-03-01 23.17.56.png

  • hoverしたときは背景とほぼ同じ色が利用される
  • ソート後にマウスを項目から離した後も、MuiTableSortLabel-active の背景色が黒 のが理由なので、Material UIのテーマをオーバライドして MuiTableSortLabel のスタイルを付けます。
  const theme = createMuiTheme({
    overrides: {
      MuiTableSortLabel: {
        root: {
          color: "#fff",
          "&:hover": {
            color: "#fff !important"
          }
        },
        active: {
          color: "#fff !important"
        }
      }
    }
  });

ここではとりあえず表示させたいので !important を付けて優先順位をあげましたが、StackOverflowの2件目の回答『Solution for your problem is following:』も良さそうです。

ソートアイコンのカスタマイズ

ソートアイコンのサイズが大きい、背景色やスタイルによってはソート操作後にアイコンが見にくい、といった時はカスタマイズできます。
DevelopersIOさんのこちらの記事の通りです。

動作サンプル

CodeSandboxのサンプルです。このページの説明の『案2 API返却結果をUI側でソートする』と『スタイルの調整』を実装しています。
修正していろいろな挙動を試してみてください。
https://codesandbox.io/s/material-table-remote-data-sort-demo-vgfhm?file=/src/App.js

参考

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

TypeScriptをなんとなく理解する② ~型を組み合わせる: Union~

この記事は何?

TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第2弾です!

今回は、型を組み合わせて新たな型を生成する、
Unionについて見ていきます。

過去の記事、続編も合わせて御覧ください!
JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
・ TypeScriptをなんとなく理解する② ~型を組み合わせる: Union~ ← この記事
TypeScriptをなんとなく理解する③ ~型を組み合わせる: Generics~
・ TypeScriptをなんとなく理解する④ ~構造型~ (3/3更新予定)

型の組み合わせとは?

TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。

この記事では、Unionの概要について説明していきます。

Unionによる型の作成

unionの基本

まずは、Unioinを使わずに型を作ってみます。
型はtypeを利用して、以下のように作成します。

type Fish = "sanma";

新しくFish型を作成しました。
このFish型は、文字列(string)のsanmaのみを許容する型です。
そのため、この型を使って以下のように、定数lunchを定義できます。

type Fish = "sanma";
const lunch: Fish = "sanma";

lunchFish型なので、"sanma"のみ許容します。
つまり、ランチにはサンマしか食べることが出来ません。
実際にlunchにサンマ以外を定義しようとすると...

const lunch: Fish = "iwana"; // タイプ '"iwana"'はタイプ '"sanma"'に割り当てることができません。ts(2322)

Fish型のlunchには、"iwana"を定義することが出来ませんでした。
ランチには、サンマもイワナも食べるので、どちらも許容したいです。
そこで、Unionを利用します。Union|を利用して次のように記述します。

type Fish = "sanma" | "iwana";
const lunch: Fish = "iwana";

このように|を利用してFish型に"sanma", "iwana"に絞る事ができます。

ちなみに、"sanma", "iwana"以外は許容されません。

type Fish = "sanma" | "iwana";
const lunch: Fish = "iwana";
const dinner: Fish = "sanma";
const breakfirst: Fish = "saba"; // タイプ '"saba"'はタイプ 'Fish'に割り当てることができません。

もちろん数値も扱うことができます。

type AndroidVersion = 9 | 10 | 11;
const myAndroidOsVersion: AndroidVersion = 11;

ちょっと発展した型の作成

以上では、明確な文字や数値に限定して、型を作成しました。
ここまで厳格にしなくても、「文字列stringはOK!」「文字列の入った配列string[]はOK!」ということも出来ます。

具体的には、以下のような記述方法です。

type Food = string | string[];

const branch: Food = 'cake';
const nightMeal: Food = ['ramen', 'onigiri', 'potechi'];
const theLastSupper: Food = ['ramen', 'onigiri', 1498]; // タイプ「number」はタイプ「string」に割り当てることができません。ts(2322)

レオナルド・ダ・ヴィンチ作の最後の晩餐(theLastSupper)は、1498年に完成しましたが、これは数値であり、文字列でないのでエラーが起きます。

ちなみに、このようなUnionで型を定義した際に便利なのが型のチェックです。

以下のように、型のチェックをすることができます。

タイプ 述語
文字列 typeof s === "string"
数値 typeof n === "number"
ブール値 typeof b === "boolean"
未定義 typeof undefined === "undefined"
関数 typeof f === "function"
配列 Array.isArray(a)

これを使って、型によって条件分岐をすることが出来ます。

const healthCheck = (food: Food): string => {
  if (typeof food === "string") {
    return `it's OK`;
  } else {
    return `it's Bad`;
  }
};

console.log(healthCheck(["ramen", "onigiri", "potechi"])); // it's Bad

おわりに

今回はUnionを使って、型を結合してみました!
型を組み合わせて、自分だけのユニークな型を作ってみましょう!

「そういえば、前回、Interfaceが出てきたけどtypeと何が違うの...?」と思った方もいるかもしれません。今後、この違いに関しても書く予定ではあります。が、既に記事があるので一旦こちらにご案内します...! → TypeScript の Interface と Type Alias の違い

参考文献

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

TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~

この記事は何?

TypeScriptとは何か、何がいいのかを伝えようと頑張った記事の第2弾です!

今回は、型を組み合わせて新たな型を生成する、
Unionについて説明していきます。

@ 過去の記事、続編?も合わせて御覧ください!
 1. JavaScriptを知っている方がTypeScriptをなんとなく理解するための記事① ~はじめに~
 2. TypeScriptをなんとなく理解するための記事② ~型を組み合わせる: Union~ ← この記事
 3. TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~
 4. TypeScriptをなんとなく理解するための記事④ ~構造型~
@ TypeScriptがなんとなく理解できたら、是非インストールして、触ってみてください!
 ? 【画像で説明】シンプルにTypeScriptを導入して使う方法

型の組み合わせとは?

TypeScriptでは、型と型を組み合わせて新しく型を作り上げることが出来ます。
この型の組み合わせの方法は2つあります。
1つは、Union、もう1つは、Genericsです。

この記事では、Unionの概要について説明していきます。

※Genericsについては次回の記事で説明しています。

Unionによる型の作成

unionの基本

まずは、Unioinを使わずに型を作ってみます。
型はtypeを利用して、以下のように作成します。

type Fish = "sanma";

新しくFish型を作成しました。
このFish型は、文字列(string)のsanmaのみを許容する型です。
そのため、この型を使って以下のように、定数lunchを定義できます。

type Fish = "sanma";
const lunch: Fish = "sanma";

lunchFish型なので、"sanma"のみ許容します。
つまり、ランチにはサンマしか食べることが出来ません。
実際にlunchにサンマ以外を定義しようとすると...

const lunch: Fish = "iwana"; // タイプ '"iwana"'はタイプ '"sanma"'に割り当てることができません。ts(2322)

Fish型のlunchには、"iwana"を定義することが出来ませんでした。
ランチには、サンマもイワナも食べるので、どちらも許容したいです。
そこで、Unionを利用します。Union|を利用して次のように記述します。

type Fish = "sanma" | "iwana";
const lunch: Fish = "iwana";

このように|を利用してFish型に"sanma", "iwana"に絞る事ができます。

ちなみに、"sanma", "iwana"以外は許容されません。

type Fish = "sanma" | "iwana";
const lunch: Fish = "iwana";
const dinner: Fish = "sanma";
const breakfirst: Fish = "saba"; // タイプ '"saba"'はタイプ 'Fish'に割り当てることができません。

もちろん数値も扱うことができます。

type AndroidVersion = 9 | 10 | 11;
const myAndroidOsVersion: AndroidVersion = 11;

ちょっと発展した型の作成

以上では、明確な文字や数値に限定して、型を作成しました。
ここまで厳格にしなくても、「文字列stringはOK!」「文字列の入った配列string[]はOK!」ということも出来ます。

具体的には、以下のような記述方法です。

type Food = string | string[];

const branch: Food = 'cake';
const nightMeal: Food = ['ramen', 'onigiri', 'potechi'];
const theLastSupper: Food = ['ramen', 'onigiri', 1498]; // タイプ「number」はタイプ「string」に割り当てることができません。ts(2322)

レオナルド・ダ・ヴィンチ作の最後の晩餐(theLastSupper)は、1498年に完成しましたが、これは数値であり、文字列でないのでエラーが起きます。

ちなみに、このようなUnionで型を定義した際に便利なのが型のチェックです。

以下のように、型のチェックをすることができます。

タイプ 述語
文字列 typeof s === "string"
数値 typeof n === "number"
ブール値 typeof b === "boolean"
未定義 typeof undefined === "undefined"
関数 typeof f === "function"
配列 Array.isArray(a)

これを使って、型によって条件分岐をすることが出来ます。

const healthCheck = (food: Food): string => {
  if (typeof food === "string") {
    return `it's OK`;
  } else {
    return `it's Bad`;
  }
};

console.log(healthCheck(["ramen", "onigiri", "potechi"])); // it's Bad

おわりに

今回はUnionを使って、型を結合してみました!
型を組み合わせて、自分だけのユニークな型を作ってみましょう!

「そういえば、前回、Interfaceが出てきたけどtypeと何が違うの...?」と思った方もいるかもしれません。今後、この違いに関しても書く予定ではあります。が、既に記事があるので一旦こちらにご案内します...! → TypeScript の Interface と Type Alias の違い

Next: TypeScriptをなんとなく理解するための記事③ ~汎用的な型を作成する: Generics~

参考文献

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

EslintとPrettierをReactプロジェクトで使用(備忘録)

目的

公式では基本的には非推奨となっているみたいなので、
"eslint-plugin-prettier"を使わずに、Reactで作成したプロジェクトをVSCodeで保存時に
フォーマットの自動整形と構文チェックをする。

前提

  • node.jsとyarnはインストール済み
  • npx create-react-appでプロジェクト作成
    (この時、eslint 系パッケージはreact-scriptsに含まれている。yarn.lockで確認。)
  • Typescriptはここでは使用せず

  • VS Code extensionsにて eslintとprettierを導入済み

    (VSCode 拡張用の prettier は、node_modules に prettier がインストールされていれば、そちらを実行するようになっている。ここではCLIでコマンドで実行させずに、エディタ上で自動整理を実行させるために、インストールしておく。)

    https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint

    https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

手順

1, npx create-react-app sample
2, cd sample
3, yarn add -D eslint prettier eslint-config-prettier
4, yarn run eslint --init  :eslintrc.jsonファイル作成 (質問に答えていきます)
5, 各ファイルを編集  (prettierrc.jsonは作らず、デフォルトのままになってます。)

eslintrc.json
{
  "env": {
    "browser": true,
    "es2021": true,
    "jest": true 
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:prettier/recommended",
    "prettier" //eslintのフォーマットの機能を上書きして機能の競合をなくす
  ],
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["react","prettier"],
  "rules": {
    "react/react-in-jsx-scope": "off",
    "prettier/prettier": "error"
  }
}

settings.json
{  *省略

 //liner
  "editor.codeActionsOnSave": { "source.fixAll.eslint": true },
  "eslint.alwaysShowStatus": true,
  "eslint.lintTask.enable": true,

  //formatter
  "editor.formatOnSave": true,
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.formatOnPaste": true,
  "prettier.packageManager": "yarn",
  "javascript.format.enable": false,

  "files.autoSave": "onFocusChange",
}

参照

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

jsxでアポストロフィを使う【eslintreact/no-unescaped-entities】

問題

Reactのjsx内にアプストロフィを含む文字列を記述しようとしたらESLintのエラー(eslintreact/no-unescaped-entities)に引っかかった。
スクリーンショット 2021-03-02 15.07.34.png

解決方法

文字列の'(アポストロフィ)の箇所を&apos;,&lsquo;,&#39;,&rsquo;のどれかに変換する。

<Link href="/user/signUp" variant="body2">
    Don't have an account? Sign up           // エラー
    Don&apos;t have an account? Sign up     // OK
    Don&lsquo;t have an account? Sign up   // OK
    Don&#39;t have an account? Sign up     // OK
    Don&rsquo;t have an account? Sign up   //OK
</Link>

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

Material UIをTailwind CSSのスタイルで上書きする方法

結論から言うと、Tailwind CSSの設定ファイルにimportant: trueを追記します(公式)。

tailwind.config.js
module.exports = {
  .
  .
  .
  variants: {
    extend: {},
  },
  plugins: [],
+ important: true,
}

こうすることで、className属性に指定したTailwind CSSが既存のスタイルの一部(あるいは全て)を上書きしてくれます。


Material UI便利ですよね。でも、使っているうちに既存のスタイルを少し修正したいときがあります。
例えばAvatarコンポーネントは、画像をいかにもアイコン風な感じに切り取ってくれる点が優秀だと感じていますが、もし既存のサイズより小さくカスタマイズしたい場合次のように書く必要があります。

react.tsx
import {
  Avatar,
  makeStyles,
  createStyles,
  Theme,
} from "@material-ui/core";
react.tsx
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    small: {
      width: theme.spacing(2), //= '16px'
      height: theme.spacing(2), //= '16px'
    }
  })
);
react.tsx
const classes = useStyles();
return (
  <Avatar className={classes.small}/>
);

Tailwind CSSで書く場合、冒頭の設定を行ったならばこれだけで済みます。

react.tsx
return (
  <Avatar className="w-4 h-4"/> //= 'width: 16px, height: 16px' 
);

なおimportant: trueはTailwind CSSで記述した全てのクラスが!important宣言付きでマークアップされることをtrueにするものですので注意してください。

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

Function Component の useEffect 内の Promise 内の関数を spyOn する

  • 理論上可能そうだったので、ひたすら試行錯誤してたらできたのでメモがてら
  • これを作った目的は API を叩けない状況下で、叩いたことにしてテストを進める方法を調べてたから
    • HTTP 通信そのものをモックして、その結果に応じた処理が想定通り走っているかどうかを確認したい
  • create-react-app で作成した TypeScript React のプロジェクトに npm t を掛けてテストすることを想定してます

確認環境

Env Ver
react 17.0.1
react-scripts 4.0.2
typescript 4.1.3

確認内容

検証対象

  • ページ読み込み時に一回だけ API を蹴ってなんかすることを想定しています
AsyncHookExample.tsx
import axios from 'axios';
import { useEffect } from 'react';

export const AsyncHookExample = () => {
  useEffect(() => {
    axios
      .get('https://localhost/')
      .then((res) => console.log(res))
      .catch((err) => console.error(err));
  }, []);

  return <p />;
};

検証方法

  • axios をモックした上で、モック関数の Promise の解決を待ち、 Promise のコールバックを spyOn して検査する内容です
    • モック関数の Promise の解決法を求めるのにハマりましたが、これは foo.mock.results[0].value に対して await expect(foo.mock.results[0].value).resolves としてやればいけます
    • 細かいことはソースコードのコメントに書いてます
AsyncHookExample.spec.tsx
import { render } from '@testing-library/react';
import axios from 'axios';
import { AsyncHookExample } from './AsyncHookExample';

// `axios` をモックにする
jest.mock('axios');
// `axios` のモックを取得
const mockedAxios = axios as jest.Mocked<typeof axios>;
// `console.log()` を `spyOn'
const spyConsoleLog = jest.spyOn(console, 'log');
// `console.error()` を `spyOn'
const spyConsoleError = jest.spyOn(console, 'error');

describe('AsyncHookExample', () => {
  it('resolve promise in Hook', async () => {
    // テストに期待する結果
    // `axios` はモックなので値は適当
    const beResult = {
      status: 200,
      data: null,
    };
    // `axios` のモックが `resolve` する値を設定
    mockedAxios.get.mockResolvedValue(beResult);
    // コンポーネントを `render` して `useEffect` を走らせる
    render(<AsyncHookExample />);
    // `axios.get()` が呼ばれたことを確認
    expect(mockedAxios.get).toBeCalled();
    // モックの結果を取得
    const testResult = mockedAxios.get.mock.results[0].value;
    // `reject` された値が期待通りであることを確認
    await expect(testResult).resolves.toEqual(beResult);
    // `useEffect` の中の `Promise` の中にある `console.log()` が呼ばれたことを確認
    expect(spyConsoleLog).toBeCalled();
  });

  it('reject promise in Hook', async () => {
    // テストに期待する結果
    // `axios` はモックなので値は適当
    const beResult = {
      status: 400,
      data: null,
    };
    // `axios` のモックが `reject` する値を設定
    mockedAxios.get.mockRejectedValue(beResult);
    // コンポーネントを `render` して `useEffect` を走らせる
    render(<AsyncHookExample />);
    // `axios.get()` が呼ばれたことを確認
    expect(mockedAxios.get).toBeCalled();
    // モックの結果を取得
    const testResult = mockedAxios.get.mock.results[0].value;
    // `reject` された値が期待通りであることを確認
    await expect(testResult).rejects.toEqual(beResult);
    // `useEffect` の中の `Promise` の中にある `console.error()` が呼ばれたことを確認
    expect(spyConsoleError).toBeCalled();
  });
});

あとがき

  • mockResolvedValue のスコープやらモックの Promise を解決させるマッチャやら調べるのに手間取りました
    • あとはそもそも Promise が解決した結果がどこに入るのかとか、兎に角あれこれ
  • expect(mockedAxios.get).toBeCalled() が通るのはすぐに気づいたので、なら出来るだろうとひたすら調べてました
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useEffect 内の Promise 内の関数を spyOn する

  • 理論上可能そうだったので、ひたすら試行錯誤してたらできたのでメモがてら
  • これを作った経緯は API を叩けない状況下で、叩いたことにしてテストを進める方法を調べてたから
    • axios そのものをモックして、その結果に応じた処理が想定通り走っているかどうかを確認したい
  • create-react-app で作成した TypeScript React のプロジェクトに npm t を掛けてテストすることを想定してます

確認環境

Env Ver
react 17.0.1
react-scripts 4.0.2
typescript 4.1.3

確認内容

検証対象

  • ページ読み込み時に一回だけ API を蹴ってなんかすることを想定しています
AsyncHookExample.tsx
import axios from 'axios';
import { useEffect } from 'react';

export const AsyncHookExample = () => {
  useEffect(() => {
    axios
      .get('https://localhost/')
      .then((res) => console.log(res))
      .catch((err) => console.error(err));
  }, []);

  return <p />;
};

検証方法

  • axios をモックした上で、モック関数の Promise の解決を待ち、 Promise のコールバックを spyOn して検査する内容です
    • モック関数の Promise の解決法を求めるのにハマりましたが、これは foo.mock.results[0].value に対して await expect(foo.mock.results[0].value).resolves としてやればいけます
    • 細かいことはソースコードのコメントに書いてます
AsyncHookExample.spec.tsx
import { render } from '@testing-library/react';
import axios from 'axios';
import { AsyncHookExample } from './AsyncHookExample';

// `axios` をモックにする
jest.mock('axios');
// `axios` のモックを取得
const mockedAxios = axios as jest.Mocked<typeof axios>;
// `console.log()` を `spyOn'
const spiedConsoleLog = jest.spyOn(console, 'log');
// `console.error()` を `spyOn'
const spiedConsoleError = jest.spyOn(console, 'error');

describe('AsyncHookExample', () => {
  it('resolve promise in Hook', async () => {
    // テストに期待する結果
    // `axios` はモックなので値は適当
    const beResult = {
      status: 200,
      data: null,
    };
    // `axios` のモックが `resolve` する値を設定
    mockedAxios.get.mockResolvedValue(beResult);
    // コンポーネントを `render` して `useEffect` を走らせる
    render(<AsyncHookExample />);
    // `axios.get()` が呼ばれたことを確認
    expect(mockedAxios.get).toBeCalled();
    // モックの結果を取得
    const testResult = mockedAxios.get.mock.results[0].value;
    // `reject` された値が期待通りであることを確認
    await expect(testResult).resolves.toEqual(beResult);
    // `useEffect` の中の `Promise` の中にある `console.log()` が呼ばれたことを確認
    expect(spiedConsoleLog).toBeCalled();
  });

  it('reject promise in Hook', async () => {
    // テストに期待する結果
    // `axios` はモックなので値は適当
    const beResult = {
      status: 400,
      data: null,
    };
    // `axios` のモックが `reject` する値を設定
    mockedAxios.get.mockRejectedValue(beResult);
    // コンポーネントを `render` して `useEffect` を走らせる
    render(<AsyncHookExample />);
    // `axios.get()` が呼ばれたことを確認
    expect(mockedAxios.get).toBeCalled();
    // モックの結果を取得
    const testResult = mockedAxios.get.mock.results[0].value;
    // `reject` された値が期待通りであることを確認
    await expect(testResult).rejects.toEqual(beResult);
    // `useEffect` の中の `Promise` の中にある `console.error()` が呼ばれたことを確認
    expect(spiedConsoleError).toBeCalled();
  });
});

あとがき

  • mockResolvedValue のスコープやらモックの Promise を解決させるマッチャやら調べるのに手間取りました
    • あとはそもそも Promise が解決した結果がどこに入るのかとか、兎に角あれこれ
  • expect(mockedAxios.get).toBeCalled() が通るのはすぐに気づいたので、なら出来るだろうとひたすら調べてました
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Material UIのDrawerでサイドバーを作る最もシンプルな例

はじめに

サイドバーを作ろうと思って、Material UIのDrawerを使おうとしたとき、公式のリファレンスが載せているコードが盛り沢山でどこが最小限なのかわからん!になったので最もシンプルな形を目指してみました。

環境

  • Material UI v4.11.3
  • react v17.0.1

最もシンプルを目指してみた

import React, {useState} from 'react';
import Drawer from '@material-ui/core/Drawer';

export default function TemporaryDrawer() {

    const [open, setopen] = useState(false);
    const toggleOpen=() => {
        setopen(!open);
    }
    return(
        <>
            <button onClick={toggleOpen}>hoge</button>
            <Drawer anchor='left' open={open} onClose={toggleOpen}>
                <p>hello</p>
            </Drawer>
        </>
    );
}

これだけのコードでボタンを押して開いてサイドバー以外をクリックすると閉じるナビゲーションドロワーを実現することができます。

anchor = 'left'をtop, right, bottomにするとそれぞれ上から、右から、下から出てくるように変更できます。

上のコードでの表示をみてみるとこんな感じ

ナビゲーションドロワーの幅を指定する必要はありそうですが、あとは<Drawer />の子要素を変更するだけで素敵なナビゲーションドロワーを作れます!!

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