20210111のJavaScriptに関する記事は21件です。

AtCoder Beginners Selection 解答例【JavaScript】

AtCoder Beginners SelectionをJavaScriptで解きました。
競技プログラミングはC++で解くのがスタンダードですが、JavaScriptが好きなのであえてJavaScriptを使って解いています。
JavaScriptは標準入力がちょっと面倒なので、[]から確認してください。

※問題の解説はこちらの記事を参考にしてください。

問題 1 : A - Product

https://atcoder.jp/contests/abc086/tasks/abc086_a

解答例

function main(input) {
  var args = input.split(" ");
  var a = parseInt(args[0], 10);
  var b = parseInt(args[1], 10);

  (a * b) % 2 === 0 ? console.log("Even") : console.log("Odd");
}
main(require("fs").readFileSync("/dev/stdin", "utf8"));

問題 2 : A - Placing Marbles

https://atcoder.jp/contests/abc081/tasks/abc081_a

解答例

function main(input) {
  return console.log(input.split("1").length - 1);
}
main(require("fs").readFileSync("/dev/stdin", "utf8"));

問題 3 : B - Shift only

https://atcoder.jp/contests/abc081/tasks/abc081_b

解答例

function main(input) {
  var args = input.split("\n");
  var a = args[1].split(" ").map((n) => parseInt(n, 10));

  var sum = 0;
  while (a.every((n) => n % 2 === 0)) {
    a = a.map((n) => n / 2);
    sum++;
  }
  console.log(sum);
}
main(require("fs").readFileSync("/dev/stdin", "utf8"));

問題 4 : B - Coins

https://atcoder.jp/contests/abc087/tasks/abc087_b

解答例

function main(input) {
  var args = input.split("\n");
  var a = parseInt(args[0], 10);
  var b = parseInt(args[1], 10);
  var c = parseInt(args[2], 10);
  var x = parseInt(args[3], 10);

  var sum = 0;
  for (let i = 0; i <= a; i++) {
    for (let j = 0; j <= b; j++) {
      for (let k = 0; k <= c; k++) {
        if (500 * i + 100 * j + 50 * k === x) sum++;
      }
    }
  }
  console.log(sum);
}
main(require("fs").readFileSync("/dev/stdin", "utf8"));

問題 5 : B - Some Sums

https://atcoder.jp/contests/abc083/tasks/abc083_b

解答例

function main(input) {
  const args = input.split(" ");
  const N = parseInt(args[0], 10),
    A = parseInt(args[1], 10),
    B = parseInt(args[2], 10);

  const nums = Array.from(new Array(N), (v, i) => ++i).filter((i) => {
    var n = i.toString().split("").reduce((p, c) => p + parseInt(c), 0);
    return n >= A && n <= B;
  });
  console.log(nums.reduce((p, c) => p + c, 0));
}

main(require("fs").readFileSync("/dev/stdin", "utf8"));

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

receiptline で作った SVG の電子レシートを CodePen してみた

今回は「Webグラフィックス Advent Calendar 2020」に投稿した記事「SVG + CSS + Node.js + receiptline で電子レシートを発行してみよう」の補足です。

上記の記事では、作成した SVG の電子レシートを貼り付けることができず、仕方なくスクリーンショットを撮って PNG 形式で貼り付けていました。

ところが、翌日に投稿された、カレンダー作成者 warotarock さんの記事には、「動くもの」が CodePen で埋め込まれているではありませんか!

そこで、作成した SVG の電子レシート各種も CodePen でデモしてみたいと思います。

動くもの

See the Pen SVG + CSS + Node.js + receiptline = Digital Receipt by dopperi46 (@dopperi46) on CodePen.

何とかできました!
ハマりどころは、CodePen 埋め込みスクリプトの URL を変更しないと表示されないところ。

<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

また何か作ったら投稿します。ではまた!

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

Javascriptで外国で有名なゲーム”CONNECT4”を作ってみた

こんにちは、Mottyです。
今回もJavascriptを使ったテーマでやっていきます。

はじめに

Connect4をご存知いらっしゃる方は多いかと思いますが、簡単に申し上げますと四目並べです。
CONNECT4.jpg

先攻と後攻が交互にボード上に駒を打ち、縦横斜めのいづれかに4目以上並べれば勝ち・・・というシンプルなゲームです。
Javascriptの勉強の題材にはちょうど良いかなと思って、
自分の家にも4目並べがありますのでどなたか一緒に遊びませんか(急なお誘い)

開発の準備

・HTML/CSS/Javascript
・Node.js(デバッグ用)
・Bootstrap4(ボタン用)

描画ソフトはペイント、
開発環境はVisualStudioCodeで行いました。

全部解説すると冗長になるので、作ってみて勉強になった部分だけ載せていきたいと思います。
↓完成版はこんな感じになりました。
Screenshot.PNG

盤面の描画

ボードゲーム開発において悩んだのは、盤面をどう扱うか、ということでした。HTMLに直書きしても
よかったのですが、盤面情報を配列に持たせておいてそれを元に画面に描画していく、というような
やり方を行いました。(いわゆるModel-View分離型というやつですかね・・・?)

script.js
    var boardarray = [
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0]]


        function load_board(array)
        {
            board.innerHTML = "";
            for (let i = 0; i < array.length; i++) {
                for (let j = 0; j < array[i].length; j++) {
                    if(array[i][j] == 0)
                    {
                        board.appendChild(blank.cloneNode(true))
                    }                    
                    else if(array[i][j] == 1)
                    {
                        board.appendChild(red.cloneNode(true))
                    }
                    else if(array[i][j] == -1)
                    {
                        board.appendChild(yellow.cloneNode(true))
                    }
                }
            board.innerHTML += "<br>";
            }
        }

load_board(array)では、先攻を1、後攻を−1としております。
ユーザーの入力を受け取って適宜盤面を更新し、それを読み込むというロジックでやってますね。

勝利判定

以下、勝利判定の関数です。ユーザーが石打ちを入力するたびに稼働します。
縦横ななめのいずれかが4つ以上並んでいれば良いので、打った位置から周り4方向(実質は8方向?)確認し、
いずれかの方向において自分と同じ石の数が連続して3つ以上あれば、1(先攻)/-1(後攻)、なければ0を返すfunctionにチェックしてもらってます。

[Something went wrong]()

script.js
        function VictorysNumber(boardarr, Row, Col) //ボードと現在行・現在列を引数として代入
            //8方向を見て連続番号を管理する配列に格納していく。
            //格納した配列をもとに、勝利かどうかを判定。
            //→方向が0番、以降45°左回転するごとに番号を1番加算し、合計7番までの8方向を定義する。
            {
            var continuousNum = [0,0,0,0,0,0,0,0]//1,2,3,4,5,6,7,8番方向
            const directionList =[
            [0,1],//0番
            [-1,1],//1
            [-1,0],//2
            [-1,-1],//3
            [0,-1],//4
            [1,-1],//5
            [1,0],//6
            [1,1]] //7

            //スコープははじめ置かれた石と同じ
            var scopeRow = Row;
            var scopeColumn = Col;

        //スコープ開始

        for (let i = 0; i < 8; i++) {   
            //textlog.value += "勝利判定関数の開始。ScopeRow="+scopeRow+ "、scopeColumn = " +scopeColumn +"です。\n";
            //textlog.value += i+"番方向を見ます。";
            while(true)
            {
                //与えられた方向への移動操作
                scopeRow += directionList[i][0];
                scopeColumn += directionList[i][1];
                try
                {
                    if(0 <= scopeRow <= 5 && 0 <= scopeColumn <= 6 &&
                        boardarr[scopeRow][scopeColumn] == boardarr[Row][Col]) //起点と今見ている場所が同じであればカウントを増やす
                    {                       
                        continuousNum[i]++; 
                        //textlog.value += i +"番方向に" + continuousNum[i] + "個の連続した石が発見されました。\n";
                    }
                    else
                    {
                        //textlog.value += i +"番方向には同じ石は見つかりませんでした。\n";
                        break; //値が同じでなければ(連番でなければ)break
                    }
                }
                catch(e)
                {
                    break; //ボードを超えた値を参照するとエラーが出る。飛び越えてもbreak.
                }
            }
            //スコープを元の位置に戻す。
            scopeRow = Row;
            scopeColumn = Col;
        }
                //勝利判定
                if( continuousNum[0] + continuousNum[5] == 3 ||
                continuousNum[1] + continuousNum[6] == 3 ||
                continuousNum[2] + continuousNum[7] == 3 ||
                continuousNum[3] + continuousNum[8] == 3 )
                {
                    return turn; //勝利者決定
                }
                else
                {
                    return 0; //勝負がついていない場合は0を返す。
                }
        }

※これは以前のExcelで五目並べを作ったときとほぼ同じアルゴリズムを採用しております。

終わりに

やはりJavascriptを少しでもいいから勉強すると、View画面作成も怖じ気づかなくなりますね・・・!
今ならなんでもできそうな気がしますが、きっと気のせいで実は色々と奥が深い分野だと思います()

全ソースコードはgithubに載せております。
https://github.com/YukiYamamotty0713/connect4/

参考URL
今回は下記の動画を大変参考にさせていただきました。メモ帳を使って1時間でオセロゲームを
仕上げるという凄腕のプログラマーさんの動画です。
・ニコニコ動画・【プログラミング】オセロを1時間で作ってみた【実況解説】
https://www.nicovideo.jp/watch/sm8391299

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

javascript 正規表現 test を使って、完全一致 部分一致 をあぶり出せ

条件分岐で test をよく使います。
test は正規表現パターンと文字列の一致を true, false で返します。

完全一致、部分一致、前方一致、後方一致など、使い方を忘れやすいのでまとめます。

基本的な使い方はこれ。

/パターン/.test('検索したい文字列')

// 例 (URLに qiita が含まれるか)
const url = window.location.href
if ( /qiita/.test(url) ) {
  console.log('qiita')
}

完全一致

単数
^(先頭が一致) と $(末尾が一致)を使う。
=== を使えば良いのであまり使わないけど・・・)

/^qiita$/.test('検索したい文字列')

複数
() で囲って | でつなぐ。

/^(qiita|hoge|huge)$/.test('検索したい文字列')

部分一致

完全一致がわかれば簡単。
^$ を取るだけ。

// 単数
/qiita/.test('検索したい文字列')

// 複数
/qiita|hoge|huge/.test('検索したい文字列')

前方一致 後方一致

これも簡単。
() でくくって、
前方一致なら先頭に ^
後方一致なら末尾に $ をつける。

// 前方一致 単数
/^qiita/.test('検索したい文字列')

// 前方一致 複数
/^(qiita|hoge|huge)/.test('検索したい文字列')

// 後方一致 単数
/qiita$/.test('検索したい文字列')

// 後方一致 複数
/(qiita|hoge|huge)$/.test('検索したい文字列')

大文字小文字どちらでも一致させる

正規表現のオプション i をつける。

/qiita/i.test('検索したい文字列')
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.jsでサクッとPWA対応

はじめに

next-pwaを使って、Next.jsのチュートリアルで作ったブログをサクッとPWA対応していきます!

参考:next-pwa

作成したリポジトリ:https://github.com/NozomuTsuruta/next-pwa-sample

そもそもPWAとは

PWAはプログレッシブウェブアプリ(Progressive web apps)の略で、ウェブアプリをネイティブアプリのように使えるようにする仕組みのことです。
今回は以下を実装していきたいと思います。

  • ダウンロードみたいなのができる(ホーム画面に追加)
    Qiitaにも実装されているみたいですね!
    image.png

  • オフラインで使える(キャッシュ)

next-pwa

Next.jsで簡単にPWAを実装できるようにするライブラリです。serviceWorkerで実装するキャッシュとかを裏でやってくれます。
他で有名なものにnext-offlineがありますが、next-pwaの方が更新頻度が高く、個人的に使いやすかったので、next-pwaを使っていきたいと思います。

実際にやってみる

今回のディレクトリ構成はこんな感じです↓(Next.jsのチュートリアルで作ったのをsrcに移動しただけです)
image.png

導入

以下のコマンドでインストールします。

## npm
npm install next-pwa

## yarn
yarn add next-pwa

アイコンをサクッと作る

以下のサイトで必要なアイコンをサクッと作っちゃいましょう!
様々なファビコンを一括生成。favicon generator
image.png

manifest.jsonというファイルも作成されるので一緒にpublicディレクトリに追加します。

manifest.jsonへんしゅ

manifest.json上で説明した、ダウンロードみたいものをできるようにするために必要です。

ダウンロードしたものは下のような感じになっていると思うので自分の好みに変更します。

public/manifest.json
{
    "name":             "",
    "short_name":       "",
    "description":      "",
    "start_url":        "/",
    "display":          "standalone",
    "orientation":      "any",
    "background_color": "#fff",
    "theme_color":      "#fff",
    "orientation":      "any",
    "icons": [
        {
            "src": "/android-chrome-36x36.png",
            "sizes": "36x36",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-48x48.png",
            "sizes": "48x48",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-72x72.png",
            "sizes": "72x72",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-96x96.png",
            "sizes": "96x96",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-128x128.png",
            "sizes": "128x128",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-144x144.png",
            "sizes": "144x144",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-152x152.png",
            "sizes": "152x152",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-256x256.png",
            "sizes": "256x256",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-384x384.png",
            "sizes": "384x384",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}

それぞれの説明↓

name アプリ名
short_name アプリ名(略)
icons アイコン
start_url 開始URL
display 表示の仕方
orientation 画面の向き
background_color 背景カラー
theme_color テーマカラー
description 説明
dir 字の方向
lang 言語

参考:Web App Manifest(日本語訳)

ちなみにこんな感じに編集しました↓

public/manifest.json
{
    "name":             "myapp",
    "short_name":       "myapp",
    "description":      "this is my app",
    "start_url":        "/",
    "display":          "standalone",
    "orientation":      "portrate-primary",
    "background_color": "#fff",
    "theme_color":      "#fff",
    "dir": "ltr",
    "icons": [
        {
            "src": "/android-chrome-36x36.png",
            "sizes": "36x36",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-48x48.png",
            "sizes": "48x48",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-72x72.png",
            "sizes": "72x72",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-96x96.png",
            "sizes": "96x96",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-128x128.png",
            "sizes": "128x128",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-144x144.png",
            "sizes": "144x144",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-152x152.png",
            "sizes": "152x152",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-256x256.png",
            "sizes": "256x256",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-384x384.png",
            "sizes": "384x384",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}

_document.tsxの追加

src/pagesディレクトリに_document.tsxを作成し、以下のようにHeadのなかでアイコン、manifest.jsonなどを読み込んでいきます。
必要最低限のサイズのアイコンを読み込んでいるので、お好みで追加してください。WindowsやSafariに対応しなくても良い時も好みで減らしてみてください。

src/pages/_document.tsx
import Document, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
  DocumentInitialProps,
} from "next/document";

class MyDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext
  ): Promise<DocumentInitialProps> {
    return await Document.getInitialProps(ctx);
  }

  render() {
    return (
      <Html lang="ja-JP" dir="ltr">
        <Head>
          {/* windows */}
          <meta
            name="msapplication-square70x70logo"
            content="/site-tile-70x70.png"
          />
          <meta
            name="msapplication-square150x150logo"
            content="/site-tile-150x150.png"
          />
          <meta
            name="msapplication-wide310x150logo"
            content="/site-tile-310x150.png"
          />
          <meta
            name="msapplication-square310x310logo"
            content="/site-tile-310x310.png"
          />
          <meta name="msapplication-TileColor" content="#000" />
          {/* safari */}
          <meta name="apple-mobile-web-app-capable" content="yes" />
          <meta name="apple-mobile-web-app-status-bar-style" content="#000" />
          <meta name="apple-mobile-web-app-title" content="myapp" />
          <link
            rel="apple-touch-icon"
            sizes="180x180"
            href="/apple-touch-icon-180x180.png"
          />
          {/* 一般 */}
          <meta name="application-name" content="myapp" />
          <meta name="theme-color" content="#000" />
          <meta name="description" content="this is myapp" />
          <link rel="icon" sizes="192x192" href="/icon-192x192.png" />
          <link rel="icon" href="/favicon.ico" />
          <link rel="manifest" href="/manifest.json" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

next.config.jsの作成

ルートディレクトリにnext.config.jsを作成し、以下のように記述します。

node_modulesのnext-pwaにcache.jsというファイルがあり、そこにキャッシュの設定が書かれているのでyarn buildするとキャッシュの設定が登録されます。(デフォルトではyarn devではキャッシュされません)
カスタマイズしたい場合は下のnext.config.jsのコメントみたいな感じで、runtimeCachingの配列に書きます。

next.config.js
const withPWA = require("next-pwa");

module.exports = withPWA({
  pwa: {
    dest: "public", // swの出力ディレクトリ
    // runtimeCaching: []
  },
});

ここまでで実装が完了しました。

実際に実行してみる

以下のコマンドを実行します。

## npm
npm build 
npm start

## yarn
yarn build 
yarn start

localhostで開き、デベロッパーツールを見るとServiceWorkerが登録されており、CacheStorageにも追加されていることがわかります。
一回読み込んでしまえば、オフラインにして再読み込みしてもちゃんと読み込まれます。これでオフラインで動作する確認はできました!
image.png

さらに、右上に+みたいなのが表示されていると思います。
それを押すと下のような画面が出てきます。
image.png

インストールを押すと、下のように追加され、開くとアプリのようにページが開かれます!
もちろんこれもオフラインで動作します。
image.png

image.png

これにて簡単なPWAの実装は完了です。お疲れ様でした!

ちなみにデプロイしたサイトでiPhoneのホームに追加をやるとこんな感じになります↓
image.png
開く↓
image.png

ほぼネイティブアプリじゃん?

作成したリポジトリ:https://github.com/NozomuTsuruta/next-pwa-sample
vercelでデプロイしたサイト:https://next-pwa-sample.vercel.app/

最後に

ここまで読んでくださりありがとうございます!少しでもPWAの理解のお役に立てたら嬉しいです。
ご意見・ご感想お待ちしております!

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

発狂したときにすべてを爆発できるChrome拡張機能を作った話

こんにちは、Yuiです。

皆さんは自分が書いた過去の記事とかを読み返したときに、あああああああ恥ずかしい〜〜〜〜〜全部爆発させたい〜〜〜〜〜〜と思ったことありませんか?

あるいはちょっとむずかしい記事を読んだときとか、難しいんだよ!!全部爆発させてやろうか??????とキレたことはありませんか??

ありますよね??
ありますね。

ということで、今回そんな全人類の要望を叶えるchromeの拡張機能を作ったのでその紹介をします。

どんな機能か

こちらのツイートにある通り、クリックしたら爆弾が置かれて、500ミリ秒後にその爆弾が破裂してそこにある文字を吹き飛ばすような機能です。

ただ、このツイートをしたあとに気がついたんですが、このままだと拡張機能を自分でオフにしない限り常に爆弾が置かれてしまうので大変鬱陶しいです。

例えば、起動していることを忘れて、とある記事の一部をコピーしたいな〜〜なんて思った暁にはこうなります。

Image from Gyazo

ストレス発散のために作ったのに、これではストレスが溜まってしまいますね。

ということで、公開する前に以下の機能を追加でつけることにしました。

  • デフォルトでは爆発機能をオフにしておく
  • アイコンをクリックするとポップアップが出て爆弾を置けるようにするか選択させる
  • ユーザーのブラウザ言語を取得してポップアップを自動で英語又は日本語で表示する(無駄機能)

インストール方法

只今審査中です。公開されたらこちらにリンクを貼ります!

Chromeの拡張機能なので基本Chromeでしか使えません。
調べるとこちらで色々やればfirefoxでも使えるようになるみたいでしたが、なんだかめんどくさそうなので今回は割愛します。

使い方

使い方は非常に簡単です。

  • この拡張機能をインストールする
  • 爆発させたい記事を見つける
  • 爆弾アイコンをクリックして、OKをクリック
  • あとは爆弾を置くだけ!

試しに、こちらの弊記事「友達がいなくて寂しいので友達を作った(LineBot)」を爆発させてみましょう。。

Image from Gyazo

見事このように木っ端微塵にできました。(爆弾を10個ぐらいおいたあと↓)

image.png

利用上の注意

利用にあたって、いくつかUI面でクソな部分があります。

  • ポップアップのOKを押したあとにすぐに爆弾を置けない。

これはbackground.jsでcontent_scriptsを読み込みさせてるからですかね..。多分1クリック目でjsファイルを読み込むので、実際に爆弾を置けるのは2クリック目以降になるようです。
早く爆発させてくれよ!こっちは発狂しそうなんだよ!という皆さんには非常に申し訳ないのですが、そこは一息ついて、OKを押したあとに一度どこか画面をクリックしてから、思う存分爆弾を置いて貰えればと思います。

  • 一度読み込むとリロードするまで止まらない

これは私の書き方がまずいのかもしれないんですが、アイコンのクリックでオンオフの切り替えをしようとしたところ、オフからオンにはできたんですが、オンからオフにはできなかったので、もし止めたい!となったらリロードしてください。笑
↑もちろんこれは同じページ内の話です。一度起動をしても、違うページの場合はまた再度オフからのスタートになるので安心してください。

  • 逆に常に起動ができない

今回、デフォルトでは爆弾を置くためのjsファイルは読み込まないようになっています。
なので、どんなページでも常に爆発させたい!全部を爆発してやる!という要望には答えられず申し訳ないのですが、毎回クソなページを見つけたたびにアイコンをクリックしてください。(常についてたら鬱陶しいと思ったのでこれは仕様上あえてこうしてます。)

  • リロード後、また同じページを爆発させたいとなったときにアイコンをクリックしてもポップアップが再度開かない。

これは私がbrowser_action部分の設定をあまり理解してないからですね..。一度止めてしまうと、スーパーリロードをかけても再度同じページを爆発ができないんですよね..。でも、安心してください、別タブで開き直すと再度爆弾がおけます。

簡単に技術的な話など

まず、chromeの拡張機能に必要なmanifest.jsonはこんな感じで書いています。

manifest.json
{
  "name": "Site Bomb",
  "version": "1.0",
  "description": "You can drop bombs on any pages",
  "permissions": ["activeTab", "http://*/*", "https://*/*"],
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "icons": {
    "16": "16.png",
    "48": "48.png",
    "128": "128.png"
  },
  "browser_action": {
    "default_title": "Site Bomb"
  },
  "author": "YuikoIto",
  "manifest_version": 2
}

多言語化を本格的に行うためには、こちらで書いてあるとおり、_localesディレクトリ内でファイルをそれぞれ作らないといけないみたいなんですが、今回はポップアップの言語をちょっと変えればいいだけだったので、もっと簡単にbackground.jsでこのように書くことにしました。

background.js
const browserLanguage = function () {
  const ua = window.navigator.userAgent.toLowerCase();
  try {
    // chromeは以下で利用者のブラウザ言語を取得できる
    if (ua.indexOf("chrome") != -1) {
      return (
        navigator.languages[0] ||
        navigator.browserLanguage ||
        navigator.language ||
        navigator.userLanguage
      ).substr(0, 2);
    }
    // それ以外(例えばIEなど)は下記で取得する必要がある
    else {
      return (
        navigator.browserLanguage ||
        navigator.language ||
        navigator.userLanguage
      ).substr(0, 2);
    }
  } catch (e) {
    return undefined;
  }
};

let toggle = false;
chrome.browserAction.onClicked.addListener(function (tab) {
  if (!toggle) {
    explosion();
  }
});

function explosion() {
  if (browserLanguage() == "ja") {
    if (window.confirm("爆弾を置く! \n (最初だけ2回クリックしてください)")) {
      toggle = true;
      chrome.tabs.executeScript({ file: "explosion.js" });
    }
  } else {
    if (window.confirm("Drop bombs on this page! \n (Click twice for the first time only)")) {
      toggle = true;
      chrome.tabs.executeScript({ file: "explosion.js" });
    }
  }
}

重複してるので、あんまりよくないな〜とは思いつつ、開き直ってこんな感じで簡単に書いちゃいました。

chrome.tabs.executeScript({ file: "explosion.js" });この部分で今回の肝となるexplosion.jsを読み込んでいます。

explosion.jsに関しては約400行あるのでここで書くことはやめておきますが、ロジックだけ説明すると、以下のような感じです。

  • まずページの文字をtext.split("");で一文字ずつ別々でラップします。
  • 上記の文字にtranslaterotateを別途与えてそれぞれにcssを追加しています。
  • 常に上記の文字が上に来るようにラップする際にデフォルトでz-indexを設定しています。

上記に合わせて別途.split("&nbsp;")でそれぞれの行に合わせても取得して、できるだけ元の文章の改行に変更しないようにしてたりするんですが、どうしても1文字ずつラップをしている関係でこのjsファイルを読み込むと文字間隔などが若干変わったりします...。
まあどうせすぐに爆発させるので関係ないですね!

この実装のために、こちらのjsfiddleのコードを参考にしました。→http://jsfiddle.net/dNXVx/37/

こちらの質問ですごい人が返答してくれていた部分です。

リポジトリとか(追記)

こちらに書いてます。→ https://github.com/YuikoIto/explosion_chrome_extension
作ってから気がついたんですが、chromeの拡張機能って、普通にフレームワーク入れることできたんですね...(それはそう)
素のjsでゴリゴリDOMを生成する方向で書いてたんでちょっと(いやかなり?)冗長ですが、フレームワーク作ってまとめたらもっと簡潔だったかもですね.....!
せめてファイルを分ければよかったかもしれない〜〜〜ごめんなさい。

最後に

色々不備はありますが、リリースされたらぜひ使ってみてください!
現在審査中なので近日中には公開されるはずです!(多分...きっと...)

参考

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

フロントエンド開発者のための便利なオンラインツール11選

本記事は、Chidume Nnamdi氏による「11 Useful Online Tools for Frontend Developers」(2020年2月11日公開)の和訳を、著者の許可を得て掲載しているものです。

フロントエンド開発者のための便利なオンラインツール11選

私が愛用するオンライン開発ツール

Image for post

インターネット上には、フロントエンド開発者の生活を楽にしてくれる素晴らしいツールが沢山あります。この記事では、私が開発の仕事で頻繁に使っている11のツールについて簡単に説明します。

1. CanIUse

Web APIが特定のブラウザで互換性があるのか、モバイルブラウザで使用できるのか、分からないことはありませんか?このオンラインツールを使えば、ブラウザの互換性についてWeb APIを簡単にテストできます。

Can I use―HTML5、CSS3などの対応表。
「Can I use」は、デスクトップとモバイルにおけるフロントエンドWebテクノロジーを支援する、最新のブラウザ対応表を提供します。
caniuse.com

Web Share APIのnavigator.share(...)に対応しているブラウザとバージョンを知りたいとします。

Image for post

結果を見てください。navigator.share(...)に対応しているブラウザとバージョンがすべて一覧になっています。

2. Minify

アプリコードのバンドルサイズを縮小するために、ここで空白文字やデッドコードなどを削除します。その結果、アプリのバンドルサイズが大幅に縮小され、ブラウザでの読み込み時間が短縮されます。

このオンラインツールminify.comで、Webアプリのコードを縮小できます。

3. Bit.dev

Bit.devは素晴らしいコンポーネントハブです。私は、さまざまなプロジェクトで再利用可能なコンポーネントをホストし、文書化し、管理するため、これを使っています。コードの再利用を増やし、開発を迅速化し、チームの共同作業を最適化するのに良い方法です。

また、設計システムをゼロから構築するのにも良い代替手段です(設計システムに必要なものが基本的にすべて揃っているため)。Bit.devは、コンポーネントの分離と公開を担うオープンソースツールBitと完全に連携します。

Bit.devは、React、React with TypeScript、Angular、Vueなどに対応しています。

Image for post

例:Bit.devで共有Reactコンポーネントを検索する

Bit―再利用可能なコードコンポーネントをチームで共有。
チームでより迅速に構築するため、プロジェクトとアプリケーション間で、再利用可能なコンポーネントを簡単に共有します。共同開発。
bit.dev

4. Unminify

このツールは、minifyと逆のことをします。

Unminify
このツールは、見苦しいJavaScript、CSS、HTMLコードを縮小、再フォーマット、再インデントして、再び読めるようにします。
unminify.com

このツールで、縮小されたJavaScriptコードを解凍し、明瞭にし、整えることで、再び読めるようにできます。

5. Stackblitz

これは誰もが知っているツールです。Stackblitzは、世界で最も人気で、最も使用されているIDEのVisual Studio CodeをWeb上で使用する機能を提供してくれます。

StackBlitz―Webアプリ用オンラインコードエディタ。Visual Studio Code搭載。
stackblitz.com

Stackblitzは、Angular、React、Vue、Vanilla、RxJS、TypeScriptのプロジェクトをワンクリックで素早く、スキャフォールドしてくれます。

Stackblitzは、現在のJavaScriptフレームワークのコードや機能をブラウザから試してみたい時に特に便利です。Angularの記事を読んでいて、試してみたいコードを見つけたとします。ブラウザを最小化し、数行だけで新規Angularプロジェクトをスキャフォールドできます。

速くて簡単です。

他にも素晴らしいオンラインIDEはありますが、Stackblitzの勝因は、誰もが使い慣れていて、誰もが好きなツールVisual Studio Codeフィールを使用したことだと思います。

6. JWT.io

JSON Web Token(JWT)を使用してアプリを保護している時、またはユーザがバックエンドで保護されたリソースにアクセスできるようにしている時。

ルートやリソースにアクセスする必要があるかを判断する1つの方法は、トークンの有効期限を確認することです。JWTをデコードしてペイロードを確認したいことがありますが、jwt.ioでそれがまさにできるようになります。

JWT.IO
JSON Web Token(JWT)は、2者間で転送されるクレームを表現するための、コンパクトでURL safeな方法です。
jwt.io

このオンラインツールを使用すると、トークンをプラグインしてペイロードを確認できます。 トークンを貼り付けると、jwt.ioはトークンをデコードし、そのペイロードを表示します。

7. BundlePhobia

node_modulesのサイズがわからないことや、マシンにインストールされるpakckage.jsonのサイズを知りたいことはありませんか?BundlePhobiaで分かります。

BundlePhobia
Bundlephobiaは、フロントエンドバンドルにnpmパッケージを追加した時の、パフォーマンスへの影響を調べるのに役立ちます。
bundlephobia.com

このツールで、package.jsonファイルをアップロードすると、package.jsonからインストールされる依存関係のサイズを表示します。

8. Babel REPL

Babelは無料のオープンソースJavaScriptトランスコンパイラで、最新のECMAScriptコードを古いプレーンなES5 JavaScriptに変換するために使用されます。

このツールは、Babeljsチームが設定したオンラインWebアプリで、ES6+コードをES5にトランスパイルできます。

Babel―次世代JavaScript用コンパイラ。
babeljs.io

これにより、ECMAScriptに最近追加された機能や、追加段階にあるいくつかの機能を試すことができます。コードを整え、ファイルサイズを制限し、トランスパイルの時間を節約できます。

9. Prettier Playground

Prettierは、意見の分かれるJavaScriptコードフォーマッタです。コードを解析し、JavaScriptのベストコーディングプラクティスに沿った形で、一貫したスタイルを適用します。

このツールは、開発環境で広く使用されていますが、オンラインでもコードを整えられます。

Prettier
prettier.io

10. Postman

このツールは、APIエンドポイント(GET、POST、DELETE、OPTIONS、PUT)のクイックテストに役立ちます。

私も使用したことがありますが、11選に入れる価値があります。

11. JSLint

JavaScriptの静的解析ツールでは、JSLintがトップに立っています。JSLintのオンラインバージョンを使用すると、ブラウザでJavaScriptのコードやファイルをリントできます。

https://jslint.comに行って試してみてください。

JSLint―The JavaScriptコード品質管理ツール。
JSLintはJavaScriptコード品質管理ツールです。このファイルでWebブラウザからJSLintを実行できます。ソースを受け取ることができます。
jslint.com

まとめ

他にも沢山ありますが、ここで紹介したのは私が愛用しているものです。

ご質問や、追加・修正・削除すべきものがある場合は、コメント、メール、DMなどで遠慮なくお知らせください。

ありがとう!!!

もっと詳しく知りたい方は

Reactコンポーネントを公開する方法
どのリポジトリからでもReactコンポーネントを素早く公開する方法
blog.bitsrc.io

2020年版 便利なReactコンポーネント10選
さまざまな場合のさまざまなReactコンポーネント
blog.bitsrc.io

フロントエンド開発者におすすめのツール11選
愛用しているフロントエンドツール一覧
blog.bitsrc.io

翻訳協力

Original Author: Chidume Nnamdi@ngArchangel
Original Article: 11 Useful Online Tools for Frontend Developers
Thank you for letting us share your knowledge!

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。
選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

ご意見・ご感想をお待ちしております

今回の記事はいかがでしたか?
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
皆様のメッセージをお待ちしております。

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

【React hooks】噛み砕いて解説してみた~useEffect編~

前書き

16.8vで追加された機能であるreact hooksを理解を深めるために体系的にまとめました。

以下、本題です

useEffectとは

関数コンポーネント内で「副作用」を実行できます。

そもそも副作用ってなに?

簡単に説明すると「関数の外に影響を与えてしまう関数」のことを指すことが多いです。例えば

  • console.log
  • DOMの手動変更
  • APIでのデータの取得

などのことです。「Reactにおける「副作用」とは?」の記事がわかりやすかったので一読して見ると良いかもしれません。

useEffectは実際に何ができるのか

噛み砕いて説明すると副作用(何らかの関数)を特定のタイミングで実行することができます。基本的な形をみていきましょう。

import React, { useState, useEffect } from 'react'

const Home = () => { 
  const [name, setName] = useState('')

  // ここで実際に使われています。
  useEffect(() => {
    console.log({name})
  })

  const handleName = (e) => {
    setName(e.target.value)
  }
  return(
    <>
      <h1>{name}</h1>
      <input onChange={handleName}></input>
    </>
  )
}

export default Home

注目すべきところは

useEffect(() => {
  console.log({name})
})

の部分です。useEffect関数内でこのように副作用を宣言してあげることで、レンダー時に副作用(関数)が実行されます。

以降から、どのタイミングで副作用が呼ばれるか確認していきましょう!

【1】レンダー後に実行される副作用を宣言

もっともスタンダードな形です。下記のように定義することで、副作用がレンダー(レイアウトと描画)の後に毎回実行されます。

useEffect(() => {
  console.log({name})
})

【2】アンマウント時に実行される副作用を宣言

クリーンアップ関数を実行したいときはuseEffectreturn内に書いてあげれば、アンマウント時にクリーンアップしてくれます。(公式ドキュメントからコピペしました。)

useEffect(() => {
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  }
})

クリーンアップにはイベントリスナーの削除等によく使われます。

【3】特定の条件の時に副作用を実行したい場合

この場合、useEffectの第二引数に配列の形で、何らかの値(変数等)を入れてあげてください。この値が変更された時だけ、副作用が実行されます。

下記の例では、nameの更新時に副作用(console.log)を実行するようになっています。

useEffect(() => {
  console.log({name})
},
[name]
)

【4】更新時には副作用を実行したくない場合

useEffectの第二引数に空の配列を渡してあげれば、更新時には副作用は実行されません。

useEffect(() => {
  console.log({name})
},
[]
)

以上です!

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

JavaScriptで配列から特定の値を含む要素を削除する

完全一致の場合

const array = ["ねこ", "いぬ", "とり", "さる", "キジバト"];

const indexOfDog = array.indexOf("いぬ");

array.splice(indexOfDog, 1);

部分一致の場合

const array = ["ねこ", "いぬ", "とり", "さる", "キジバト"];

const indexOfPigeon = array.findIndex(animal => animal.includes("バト"))

array.splice(indexOfPigeon, 1);

どちらもインデックスを取得してからsplice()で削除する。
findIndex()は条件式がtrueになった最初の要素のインデックスしか取得しないので注意。
splice()の第一引数は削除する要素の位置、第二引数はそこから削除する要素の数。

参考

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

緊急事態宣言下だし、人狼ゲームを実装してみた【Docker・JavaScript】

動機

春先の緊急事態宣言下ではZoom飲みが大流行したせいか、オンラインの人狼が重くなって困りました。また発令されたし、みんなが自分で人狼ゲームを起動できたら幸せなのでは?というノリで実装&公開したので紹介します。

紹介

以下のような簡単なUIのチャット人狼です。ホストの一人がDockerでゲームを立ち上げたら、ほかの人はブラウザ(スマホでもOK)からアクセスするだけで簡単に遊べます。

image.png

遊び方(ホスト)

Dockerコンテナの起動

まず以下のコマンドでDockerコンテナを起動します。初回はGitHub Container Registryからイメージがpullされます。

docker container run -it --rm -p 3000:3000 ghcr.io/dr666m1/werewolf

起動後、画面の指示に従ってゲームの設定を行ってください。
image.png

ポートの公開

3000番ポートでゲームが起動しているので、パブリックIPアドレスのある環境(GCEなど)ならみんなにIPアドレスを伝えるだけです。注意点としては、http://...:3000のようにポート番号を明示する必要があるかもしれないことと、ファイアウォールの設定を確認しておくことです。

パブリックIPアドレスのない環境ではngrokを利用するのが便利です。初期設定が完了するとngrok http 3000というコマンドで3000番ポートを公開できるようになります。コマンド後にURLが表示されるので、それをみんなに伝えてください。無料枠での制限はこちらご確認ください。

遊び方(全員)

ホストから教えてもらったURLにブラウザでアクセスしましょう。以下のような画面が表示されたら名前を決めて入場してください。

image.png

その後は普通の人狼ゲームです。役職は現状「市民」「人狼」「占い師」「霊媒師」「狩人」のみ1と標準的なので、迷うことはないと思います。

実装

Node.jsで実装しています。GitHubに全てのコードが置いてあるので興味があればご覧ください。似たようなものを作る人の参考に、利用したパッケージを簡単に紹介します。全部npm installで使えるはずです。

Next.js

Next.jsReactでUIを作成するためのフレームワークです。SSG(static generation)やSSR(server-side rendering)を利用できるのが特徴です。今回はnext exportで静的なHTMLファイルを出力するところまでNext.jsで実装しました2。GitHubリポジトリのclientディレクトリ以下が関連ファイルです。

bulma

bulmaはCSSフレームワークです。DOMのクラスを設定するだけで見た目をいい感じに整えてくれます(例えば以下はnotificationクラス)。bulmaのおかげで、今回CSSはほとんど書いていません3
image.png

Next.jsの枠組みで利用するには、_app.jsの冒頭に一行追記するだけです。

_app.js
import "bulma/css/bulma.css" // 追記

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

socket.io

socket.ioはウェブソケットを扱うパッケージで、チャットの実装に利用しました。wsというパッケージもあるのですが、socket.ioの方が多機能なので今回はそちらを使っています(例:ウェブソケットが使えない状況でロングポーリングに切り替えられる、roomnamespaceという概念でクライアントを整理できる)。ちょっと今回の実装では活かしきれなかった部分も多いのですが。

後書き

JavaScriptは全くの初心者なので、バグがあったらすみません。Zoom飲みとかにご活用ください。noteにも稀に投稿しているのでよければご覧ください(直近の記事)。


  1. 要望があれば「狂人」とかいろいろ追加するかもしれません。 

  2. custom serverを利用すればNext.jsの枠組み内で実装を完了できそうでしたが、UIに集中したくて分離しました。Dockerfileを見ての通り、多段階ビルドになっています 

  3. JavaScriptの中で多少スタイルをいじった程度です。 

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

jQueryとCSSでマウスカーソルを良い感じに作る!

最近マウスカーソルがカスタマイズされたイケイケのwebサイトをよく見かけるので、作りました。
ただUIの事を考えると、変にカスタマイズされたカーソルはストレスでしかないのでやりすぎはNGです。

作り方

以下のように2つの円形を作って、1つは通常のカーソルの動き、2つは少し遅らせ追従するような動きになるように作りました。

画面収録-2021-01-11-14.38.gif

jQueryで座標の取得とクラス追加、cssでカーソルの装飾をしていきます。

HTML

div.cursorの中に2つspanが入っていて、dot1とdot2で別々の動きを作っていく

html
<div class="cursor">
  <span class="dot1"></span>
  <span class="dot2"></span>
</div>

jQuery

  • [ $(window).mousemove ]でカーソルのホットスポットが要素内(body)で動いた時の処理
  • [ left: e.pageX , top: e.pageY ]で座標を取得
  • [ $('a').on('mouseenter', function () { } ] でaタグにホバーしたときに[active]というクラスを追加
jquery
$(window).mousemove(function (e) {
  $('.cursor span').css({
    left: e.pageX,
    top: e.pageY
  })
})
$('a').on('mouseenter', function () {
  $('.cursor span').addClass('active');
})
$('a').on('mouseleave', function () {
  $('.cursor span').removeClass('active');
})

CSS

  • [cursor: none;]でデフォルトのカーソルを非表示
  • [.cursor span { } ]の中で基本的な形(今回だと円形)を作る
  • dot1とdot2にそれぞれの形を作る
  • [.active]でホバー時の形を作る
css
* {
  cursor: none;
}
.cursor span {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  transform: translate(-50%, -50%);
  position: absolute;
  z-index: 1;
  pointer-events: none;
}
.cursor span.dot1 {
  background: rgba(98, 77, 112, 0.8);
  transition: width 0.2s, height 0.2s;
}
.cursor span.dot1.active {
  height: 50px;
  width: 50px;
  background: rgba(98, 77, 112, 0.3);
}
.cursor span.dot2 {
  height: 20px;
  width: 20px;
  border: solid 1px #624d70;
  transition: top 0.2s, left 0.2s, width 0.5s, height 0.5s;
  transition-timing-function: ease-out;
}
.cursor span.dot2.active {
  height: 80px;
  width: 80px;
}

2つ目の円形の動きの遅延

円形の1つの動きを少し遅らせて、後を追うような動きにしています。
この動きが遅い円形を作ることでマウスカーソルの良い感じが増します!
動きの作り方は、cssの[ transition: top 0.2s, left 0.2s, width 0.5s, height 0.5s; ]で指定しています。

  • 追従の遅延は [ top 0.2s, left 0.2s ]
  • 大きさの遅延は [ width 0.5s, height 0.5s ]

それぞれの数字を変えると動きを変えることができます。

transitionについて詳しくは以下
https://developer.mozilla.org/ja/docs/Web/CSS/transition

ホバーの動きをCSSだけでなく、 jQueryを絡ませる理由

aタグにホバーしたときに[::hover]ではなく[.addClass('active')]を使っています。
理由リンクだけでなく、画像やテキストにもホバー時の変化をもたせるためにjQueryでクラスを付けています。
(最初は[::hover]でしていましたが、最近jQueryでクラスを付けるようにしました。こっちのほうが都合がいい!)

ipadのようにテキスト部分にホバーすると縦棒になるようにカスタマイズ

以下のように特定の指定部分にホバーすると違う形になるようにする。

gif.gif

CSSとjQueryに以下を追加

css
.cursor span.dot1.text_active {
  height: 1.4em;
  width: 2px;
  border-radius: 0;
}
.cursor span.dot2.text_active {
  display: none;
}
jquery
$('p').on('mouseenter', function () {
  $('.cursor span').addClass('text_active');
})
$('p').on('mouseleave', function () {
  $('.cursor span').removeClass('text_active');
})

わかりやすくaタグとpタグにホバーすると形が変わるように書きましたが、実際は特定クラスを作っておいて指定する使い方のほうが良いかと思います。

まとめ

少ないコードでwebサイトの印象がガラッと変わるのでおすすめです!
僕はsassの変数を使ってデザインガイドを作っているのでjQueryとCSSを使いましたが、jsだけで作ってプラグイン化してもいいかと思います。
時間があればjsだけで完結するコードも公開します。

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

【React hooks】噛み砕いて解説してみた~useState編~

前書き

16.8vで追加された機能であるreact hooksを理解を深めるために体系的にまとめました。

以下、本題です

useStateとは

useStateを使うことで任意State変数の宣言ができます。もっとわかりやすく表現すると、関数コール内で値を保持することができます。具体的にみた方がわかりやすいと思うので、コードで見てみましょう。

import React, { useState } from 'react'

const hoge = () => {
  // 下記のコードで`State`宣言されています。
  const [name, setName] = useState('')
}

基本形はこんなイメージです。以降から詳しく解説していきます!

useStateを使ってState宣言

基本形は下記の形です。(説明のために、必要な部分だけ切り取っています。)

const [name, setName] = useState('')
  • name:任意の変数名を宣言ができます。ここではnameにしていますが、好きな名称を設定することができます。
  • setNameState変数(name)を更新するための関数を宣言しています。
  • useState(''):引数でState変数の初期値を設定することができます。例えば、useState('佐藤')みたいに設定することもできます。

State変数を読み出す。

関数コンポーネント内で下記のように呼び出すことができます。

import React, { useState } from 'react'

const hoge = () => {
  const [name, setName] = useState('')

  return(
    // 下記のように使うことができます。
    <p>私の名前は{name}です</p>
  )
}

classコンポーネントの時のようにthis.state.nameみたいにかく必要はありません。ハッピー!!

State変数を更新する

useStateで宣言した変数を更新するための関数(ここではsetState)を使ってstateを更新します。

import React, { useState } from 'react'

const hoge = () => { 
  const [name, setName] = useState('')

  const handleName = (e) => {
    setName(e.target.value)
  }

  return(
    <>
      <h1>{name}</h1>
      <input onChange={handleName}></input>
    </>
  )
}

export default hoge

少しだけ複雑になってしまっているので順番に解説していきます。

  • handleName:この関数の中でstateを更新するsetNameが更新されています。
  • onChange:このイベントリスナー内で実際にhandleNameを経由して値を更新しています。

useStateの全体像

【1】useStateをImport

import React, { useState } from 'react'

【2】useStateを使って、変数,更新関数を宣言

const [name, setName] = useState('')

nameが変数、setNameが更新関数ですね。

【3】変数を読み出し、更新

import React, { useState } from 'react'

const hoge = () => { 
  const [name, setName] = useState('')

  const handleName = (e) => {
    setName(e.target.value)
  }

  return(
    <>
      <h1>{name}</h1>
      <input onChange={handleName}></input>
    </>
  )
}

export default hoge

以上です!

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

ざっくり理解する「WebAssembly とは?」

個人的なメモ用に記述したものですので、内容の正確性は保証できかねます。

WebAssembly とは?

  • WebAssembly (Wasm) とは、ブラウザ上で動くバイナリフォーマット
  • JavaScript より高速に動作することを目標に開発・策定が進んでいる
  • C/C++、Rust、Go などの高級言語で記述して、事前にコンパイルするのが一般的
  • JavaScript から WebAssembly のロジックを呼び出すことができる

歴史

  • JavaScript も実行速度が求められるようになった
    • JavaScript は実行速度が遅い
      • 動的型付け言語のため、事前にコンパイルができず、インタプリタにより逐次的に機械語に翻訳する
      • インタプリタは、文字列を解釈し、抽象構文木 (AST: Abstract Sytactic Tree) を作成する等のオーバーヘッドが生じる
  • JIT コンパイラ (実行時コンパイラ) による高速化
    • JIT コンパイラとは?
      • JIT は Just In Time の略
      • JIT コンパイラは、実行環境に依存しない中間コードを、ソフトウェアの実行時に機械語にコンパイルする
      • JVM (Java Virtual Machine) が有名
      • 具体的には、しばらくインタープリタを用いて動作させた後、得られた統計情報から型を推定し、必要に応じてネイティブコードに変換する
    • 問題点
      • よく使うコードしかネイティブコードにならない
      • 型の推定用に統計情報が必要なため、高速に動作するまでに時間が必要
      • 型の推定に失敗することがある
  • asm.js の登場
    • JavaScript のサブセット
    • JavaScript を特定の制約に従って記述することで、型を明確にして AOT コンパイルできるようにする
      • AOT は Ahead-Of-Time の略
      • AOT コンパイルは、実行前にコンパイルすることを意味する
    • 問題点
      • ファイルサイズの増大による通信量の増加
      • ファイルサイズの増大による構文解析の時間の増加
  • WebAssembly (Wasm) へ
    • ブラウザ上で動くバイナフォーマット
    • ファイルサイズの縮小に成功し、さらに高速になった
    • フォーマットの種類
      • WASM: バイナリフォーマット
      • WAT/WAST: 人間が読む用の wasm のテキスト表現

参考資料

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

初めてOSSを作ってみて、そうだったの?と思ったことまとめ

はじめに

コロナで家にいて暇だったので、初めてOSSを作って公開しました。

言語はTypeScript、環境はWebpack、IE11とCDNに対応、リポジトリはGithubです。
ブラウザ、nodejs、コマンドプロンプト、bashで動きます。

作ってみて、「これって実はこうなってたの?」と思ったポイントを共有します。
もし同じようなOSSの公開を検討されている方がおられましたら、ご参考いただきたくと思います。

OSSについて

OSS(オープンソースソフトウェア)は、ソースコードが公開され、誰でも利用、改良、修正、再配布ができるソフトウェアのことです。

もし作ったOSSに興味があればこちらの記事をご参考ください。

NLP(日本語)で使えるJSのDateライブラリを公開しました
https://qiita.com/ShotaOki/items/5e4b02f795fbf88bf0b1

CDNに登録する

たとえばjQueryを使いたいとき、htmlのヘッダにこんな風に書いていると思います。

index.html(jQueryをインポートする)
<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>

今回作成した僕のOSSでは、以下のように書けば取り込むことができます。

index.html
<script src="https://cdn.jsdelivr.net/gh/ShotaOki/NLPDate@v0.0.1/modern/nlpdate-main.min.js"></script>

このCDNに登録するには、どこにユーザー登録して、何をすればいいのでしょう。

実のところ、何もしなくても登録されます。
githubにタグが上がっていればCDNとして利用できます。

jsdelivrのURLはhttps://cdn.jsdelivr.net/gh/${ユーザー名}/${レポジトリ名}@${タグ名}/${ファイル名}です。ghがgithubのことです。タグ名にはlatestと書いてもOKです。

タグを作ってpushする
# githubのメインブランチにタグを作る
git tag -a v0.0.1 -m 'リリースタグを作る'

# githubのメインブランチにタグをpushする
git push origin --tags

これでjsdelivrで使えるようになっています。

コマンドプロンプトでTypeScriptのファイルを動かす。

作ったOSSの都合で、どうしてもbashやコマンドプロンプトから動かしたいと思っていました。

コマンドプロンプト
REM 普通の使い方
nlp-date "明日の午後三時" -f "和暦の年月日時分秒"
>> 令和3年01月12日 15時00分00秒

REM 「明日」とか「今日」みたいにエポック秒を書けるので、ログを読むのに便利
nlp-date "1610341916の9時間後" -f "年月日時分秒"
>> 2021年01月11日 23時11分56秒

これはどうやれば実装できるのでしょう。

拡張子なしのJavaScriptファイルを作って、頭にShebang(#!で始まる行)を付けます。

nlp-date
#!/usr/bin/env node

// JavaScriptでソースを書けばよい
var NLPDate = require("../modern/nlpdate-main.min.js");
/** 
中略:引数をパースする
*/
// 標準出力にconsole.logで出力する
console.log(NLPDate(argv["value"]).asString(argv["format"]));

頭にShebangがついていると、その環境で実行します。
コマンドプロンプトはShebangが読めないので、cmdを拡張子につけたファイルから呼び出します。

nlp-date.cmd
@ECHO off
REM nodeでJavaScriptファイル(nlp-date)を実行する
node  "nlp-date" %*
EXIT /b %errorlevel%

このファイルの場所に環境変数を通せば、「nlp-date」コマンドが使えるようになります。

TypeScriptをIE11に対応する

IE11、フルネームで書くとInternet Explorer 11です。
TypeScripのts-loaderのままwebpackしても、過去の遺物 IE11で動きません。

開発元のMicrosoftがEdgeに移ったのだから、こちらとしても切り捨てたいところですが、さすがにWindowsに標準で入っているブラウザを切り捨てるわけにはいきません。

IE11向けのバンドルファイルの生成は、ts-loader -> babel-loaderの順で呼び出します。
こうすることで、IE11で対応していない関数(fetchとか)を作ってくれます。

webpack.config
{
            rules: [
                // JavaScriptをIE11向けにトランスパイルする
                {
                    test: /\.(js|jsx|ts)$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        targets: ['> 1%', 'ie 11'],
                                        useBuiltIns: 'usage',
                                        corejs: 3,
                                    },
                                ],
                            ],
                        },
                    },
                },
                // TypeScriptをJavascriptに変換する
                {
                    test: /\.ts$/,
                    exclude: /(node_modules|bower_components)/,
                    loader: 'ts-loader',
                    options: {
                        configFile: 'babel.tsconfig.json', // <- babelの時だけtsconfig.jsonを変更
                    },
                },
            ],
        }

「module変数が見つからない」と言われたら、tsconfigのmoduleをes2015に書き換えます。

babel.tsconfig.json
{
    "includes": ["src/**/*.ts", "test/**/*.ts"],
    "compilerOptions": {
        "target": "es5",
        "module": "es2015", // <- babelの時だけ"commonjs"から"es2015"に書き換える
                            // es2015だとブラウザ以外の実行(例:mochaの自動テスト)で失敗するので、
                            // webpackのoptionでbabelの時だけ変更する
        "lib": ["es6", "es2019", "dom"]
    }
}

ソースコード外にアロー関数が書かれる問題を解決するために、対応ブラウザを.browserlistrcに書きます。

.browserlistrc
> 1%
IE 11 // ここにIE 11を書けば、import文(ソースの外側)にアローが使われなくなる
not dead

これでトランスパイルすればTypeScriptのファイルがIE11対応されます。
ポリフィルも必要なものだけ取り込まれます。

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

【JavaScript】addEventListenerの使い方

プログラミング勉強日記

2021年1月11日
今日はaddEventListenerの使い方を簡単にまとめる。

addEventListenerとは

 マウスによるクリックやキーボードからの入力といった様々なイベント処理を実行するメソッド。
 一般的な書き方は、イベントの種類と実行するための関数を指定する。第1引数にイベントの種類、第2引数に関数、第3引数にイベント伝搬の方式をtrueかfalseで指定する。第3引数は通常はfalseを指定する。

基本的な書き方
対象の要素.addEventListener(種類, 関数, false);

外部の関数を設定する

 関数をイベント処理の外側で定義する。関数内の処理が複雑な場合やファイルが複数に分かれている場合に使われる。

対象の要素.addEventListener(種類, 関数, false);

function sample() {
  // 処理を記述
}

無名関数を設定する

 一般的に使われる方法で、第2引数にそのまま関数を記述する。単純な処理を記述する場合によく使われる。

対象の要素.addEventListener(種類, function() {
  // 処理を記述
}, false);

アロー関数を設定

 無名関数をES2015の書き方にしたパターンである。

対象の要素.addEventListener(種類, () => {
  // 処理を記述
});

イベントの種類

イベント種類 内容
load Webページの読み込みが完了したとき(画像などのリソースすべて含む)
DOMContentLoaded Webページが読み込みが完了したとき(画像などのリソースは含まない)
click マウスボタンをクリックしたとき
mousedown マウスボタンを押しているとき
mouseup マウスボタンを離したとき
mousemove マウスカーソルが移動したとき
keydown キーボードのキーを押したとき
keyup キーボードのキーを離したとき
keypress キーボードのキーを押しているとき
change フォーム部品の状態が変更されたとき 
submmit フォームのsubmitボタンを押したとき
scroll 画面がスクロールしたとき

サンプルコード

 クリックイベント処理の簡単なサンプルコード

<!DOCTYPE html>
<html>
  <body>

    <h1 id = "idName">クリック前</h1>
    <button id = "btn">クリック</button>

    <script>
      var btn = document.getElementById('btn');

      btn.addEventListener('click', function() {
        var mydiv = document.getElementById("idName");

        mydiv.innerHTML = "クリック後";

      }, false);
    </script>

  </body>
</html>

実行結果
image.pngimage.png

参考文献

【JavaScript】addEventListenerで関数に引数をわたす
【JavaScript入門】addEventListener()によるイベント処理の使い方!

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

javasript, jquery(ユーザー追加)

初めに

 Sakura EditorでJqueryでユーザー追加ページを作成する。

Sakura Editorのダウンロード

https://sourceforge.net/projects/sakura-editor/

HTMLページを保存する

<html>
  <head>
   <title>タイトル</title>
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
   <script> 
    $(document).ready(function(){
        // マインオブジェクト
        var $main  = $("#main-id");

        // テーブルオブジェクト
        var $table = $("<table>"); 

        // クラス定義
        var Row = function(table) { // table:変数

           // プロパティ
           var $tr        = $("<tr>");                      // 行オブジェクト
           var $td        = $("<td>").html("ユーザー名:"); // カラムオブジェクト
           var $input     = $("<input>");                   // インプットオブジェクト

           // ユーザー削除ボタン
           var $deleteUserBtn = $("<button>").html("削除");
           $deleteUserBtn.click(function() {
                this.closest("tr").remove();
           });

          // 追加メソッド
          this.addRow = function() {
            // 行を追加
            table.append($tr.append($td.append($input).append($deleteUserBtn)))
          };
        };

        // ボタンオブジェクト
        var $buttonAdd = $("<button>").html("ユーザー追加");

        // ボタンクリックイベント
        // ボタンをクリックすると、行が追加される
        $buttonAdd.click(function() {

            // インスタンス作成
            var row = new Row($table);

            // クラスのメソッドを呼ぶ
            row.addRow();
        });

        //親オブジェクトに子オブジェクトを追加
        $main.append($buttonAdd).append($table);
    });
    </script>
  </head>
  <body id="main-id">
  </body>
</html>

キャプチャ.PNG

HTMLで保存して、実行する
キャプチャ2.png

キャプチャ.PNG

無題1.png

キャプチャ2.PNG

以上

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

JavaScriptで押したボタンの文字を変更する方法

JavaScript初心者のメモとして「JavaScriptで押したボタンの文字を変更する方法」を残します。
様々な場所で使用されているボタンを押したらボタンの文字が変化する方法をメモしておきます。基本的に記載してあるコードをそのままコピーすればパソコンで実装できるように全て省略なしでコードを書いています。

ボタンを押して文字を変更する方法

ボタンを押して文字を変更するHTMLです。
クリックするボタンを作ります。
ボタンにはid="btnをつけてJavaScriptで取得できるようにしておきます。

hello.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>ToDoリスト</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>

//注目部分
  <button id="btn">変化</button>
//
  <script src="js/main.js"></script>
</body>
</html>

次にJavaScript全体のコードです。

main.js
'use strict'
 {
  const btn = document.getElementById('btn');
  btn.addEventListener('click', () => {
      btn.textContent = '押されました';
 })
}

btnのidを取得して操作ができるようにしておきます。

  const btn = document.getElementById('btn');

addEventListenerでクリックした際にどうしたいかを指定します。
今回はボタンを押したらボタンの文字が変わるように設定します。
textContentを使用し中のテキストを変更します。

btn.addEventListener('click', () => {
      btn.textContent = '押されました';

以上がボタンを押した際にボタンの文字を変更する方法です。
これ以外にボタンを押したら、他の箇所の文字を変更したりすることも可能です。
それは改めてメモしたいと思います。

最後までお読みいただきありがとうございました。

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

RhinoでJavaの中からJavaScriptを利用する 2021年版

JavaVM で JavaScript エンジンを動かして、DSLとして JavaScript を利用したいようなケースは結構あったはずだが、JavaVM に標準で搭載されているJavaScriptエンジン Nashorn はすでに Java11 で非推奨となっていて、プロダクションで使っている勢にとっては不安の種となっている。

Nashorn 非推奨の理由は、最新の ECMAScript 仕様に追随しきれないというもの。

確かにモダンなESを使いたいモチベーションはあるが、それが足枷となってJVMそのものの進化のスピードが律速されてしまうのであれば、非推奨としてJVM本体の進化のペースとは切り離すという判断は合理的。

ではどうしたらいいのか。Alt JVM として開発されている他言語対応JVM GraalVM を使うというのが第一の選択肢である。GraalVM に関しては色々なところで触れられているのでここでは触れない。

実運用上は VM を変更して運用することはないとはいえ、Javaのプログラムが特定のJVMでないと動かないというのは、なんとなく残念な感じがしてしまう。ここで試してみるのは、Nashorn 以前の JS on JVM を実現するスタンダードである Rhino である。

https://github.com/mozilla/rhino

久しぶりに Rhino のリポジトリを見てみると、最新の 1.7.13 が2020年9月にリリースされており、今でもちゃんとメンテナンスされていることがわかる。

ES2015 との互換性テーブルを見てみると、なかなか苦労している様子ではあるが、互換性のレベルも徐々に向上しているので今後にも期待が持てそうだ。

ScriptEngine で Rhino を使う

Rhino 1.7.13 で、JVM の ScriptEngine インタフェースに対応している。なるほど Nashorn をそのまま置き換えられるようになったのだ。

Rhino で ScriptEngine インタフェースを利用するには、従来の rhino.jar だけではなく rhino-engine.jar が必要となる。

そのため pom.xml に以下の依存関係を記述する。(Mavenの場合)

<!-- https://mvnrepository.com/artifact/org.mozilla/rhino -->
<dependency>
    <groupId>org.mozilla</groupId>
    <artifactId>rhino</artifactId>
    <version>1.7.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mozilla/rhino-engine -->
<dependency>
    <groupId>org.mozilla</groupId>
    <artifactId>rhino-engine</artifactId>
    <version>1.7.13</version>
</dependency>

最も単純に JavaScript を実行するには以下のようなコードになる。エンジン名にrhinoを指定するだけでそのまま利用可能だ。

ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("rhino");
try {
    scriptEngine.eval("function add(a, b) { return a + b }");
    Number v = (Number) scriptEngine.eval("add(13, 17)");
    System.out.println(v);
} catch (ScriptException e) {
    e.printStackTrace();
}

ただ Nashorn の場合は、上記コードの戻り値のNumberの実際の型はDoubleだったが、Rhinoの場合はLongに変わっている。細かいところではいろいろと差異がありそうだ。

Javaオブジェクトと相互運用してみる

JavaScript の中から Java のオブジェクトを利用するためには、JavaScript のスコープにオブジェクトをセットして呼び出す必要がある。

Map<String, Object> map = new HashMap<>();
map.put("a", 10);
map.put("b", 20);
map.put("console", new MyConsole());
SimpleBindings bindings = new SimpleBindings(map);

scriptEngine
  .eval("c = a + b;"
      + "a += b;"
      + "console.log('a=' + a + ', b=' + b + ', c=' + c);",
      bindings);

ここで MyConsole は自分で作成した以下のようなクラスだ。

public class MyConsole {

    public void log(Object arg) {
        System.out.println(String.valueOf(arg));
    }
}

結果は

a=30, b=20, c=30

となり期待するとおりとなった。

生で Rhino を使う

1.7.12 以前の Rhino と同様に、ScriptEngine インタフェースを経由せずにそのまま生で Rhino を使うことも可能だ。

この場合のコードは以下のようになる。

Context context = new ContextFactory().enterContext();
Scriptable globalScope = context.initStandardObjects();

Script script = null;
try (Reader reader = new FileReader(jsSourceFile)) {
    script = context.compileReader(reader, jsSourceFile.getName(), 1, null);
} catch (IOException | EvaluatorException e) {
    e.printStackTrace(System.err);
    System.exit(1);
}

ScriptableObject.putProperty(globalScope, "a", 10);
ScriptableObject.putProperty(globalScope, "b", 20);
ScriptableObject.putProperty(globalScope, "console", new MyConsole());

// run global scope context
script.exec(context, globalScope);

文字列の比較に注意

いくつか JavaScript で Java のオブジェクトを操作するプログラムを書いてみたところ以下のケースに遭遇した。

var javaObject = someObject.getValue(); // javaObject は java.lang.String である

console.log(javaObject); // "OK"

if (javaObject === 'OK') { // false
    // JavaScriptの文字列リテラルと同一とみなされない
    console.log('===');
} else if (javaObject == 'OK') { // false
    // == でもダメ
    console.log('==');
} else if ('OK'.equals(javaObject)) { // true
    console.log('equals');
}

この結果は equals になる。Java の文字列と JavaScript の文字列は別のものなのである。
これは注意が必要だ。

まとめ

Rhino は着々と進化しつづけている。2021年においても Java アプリケーションにおける DSL として JavaScript を採用することにおいて不安はない。

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

JavaScriptに関連ありそうな言語やライブラリ・FWを思いつく限り調べて一覧にまとめた

JavaScriptを調べていると、TypeScriptやNode.jsをはじめ、色々なライブラリやFWが数多くでてきます。

使ったことあるのも使った事ないのも一回がっつり調べようと思い、調べながら一覧にまとめました。そして、それぞれの公式サイトやWiki(日本語、英語)を読み漁り確認した気になった内容を転記しました。
だいぶ勉強になったので、これで当分は「もぉ~何が何だか分からなくなる~」状態を回避できます。
※基本公式から転記したので、間違いはないはず。

QiitaではTable化面倒なので、画像で載せます。
表形式での表記は以下のサイトで記載しています。
https://www.tsurezure-owls-nest.work/related-javasciprt-info-list/

言語

image.png

実行環境

image.png

ライブラリ・フレームワーク

image.png

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

React Redux Hooks公式ドキュメント翻訳(useSelector編)

Reactアプリケーションの状態管理のためのOSSライブラリである、React Reduxのバージョン7.1.0で追加された、Hooks APIの公式ドキュメントを翻訳していきます。

  1. React Redux Hooks公式ドキュメント翻訳(概要編)
  2. React Redux Hooks公式ドキュメント翻訳(useSelector編)

2021/1/11公開。原文リンクは以下。
・公式ドキュメント(React Redux Hooks):https://react-redux.js.org/api/hooks

今回の記事はuseSelectorAPI編です。
スクリーンショット 2021-01-10 4.38.13.png

useSelector()

const result: any = useSelector(selector: Function, equalityFn?: Function)

useSelectorを使うことでReduxのstoreのstateにデータを登録することが可能になります。

Note: useSelectorは純粋関数である必要があります。なぜなら、複数回、任意のタイミングで実行される可能性があるからです。

useSelectorは概念的にはconnect関数に対して与えた引数mapToPropsに相当します。利点としては、Reduxのstore state全体をただの引数として扱える点です。前のコンポーネントのレンダリング後から、その参照先が普遍である限り、useSelctorはキャッシュしたselectorの値を返すので、関数コンポーネントのレンダリングをいつでも実行することができます。useSelectorはさらに、Redux storeを登録したり、actionがdispatchされるときにいつでもselectorを呼び出せます。

しかしながら、useSelectorとmapState関数のそれぞれで呼び出したselectorにはいくつか違いがあります。

・selectorはオブジェクトに限らず全ての値を返すことができます。selectorの返り値はuseSelectorの返り値として扱われます。
・actionがdispatchされたとき、useSelectorは前回の返り値と今の返り値を比較します。それらが異なる場合、コンポーネントは再レンダリングを強いられます。同じ場合は、そのコンポーネントは再レンダリングされません。
・useSelector関数は引数ownPropsを受け取りませんが、propsはクロージャを通す(以下の例を見てください)またはカリー化した関数を使うことで使用することができます。
・保存されたselctor(memorized selector)を扱う場合は特別な注意が必要です。(詳細は以下の例を見てください。)
・useSelectorはデフォルトでは等価比較に厳密な===を使います。曖昧比較は使いません。(次のセクションで詳細を説明します。)

Note: propsを扱う場合は、エラーを引き起こす特別なケースがいくつかあります。詳細は使用上の注意を参照してください。

1つの関数コンポーネントでuseSelectorは複数回呼び出されることがあります。useSelectorを呼び出す際には毎回、Reduxのstoreに登録されます。React Reduxバージョン7からuseSelectorは追加されたため、同一のコンポーネントでuseSelectorの複数回の呼び出しを引き起こすようなdispatchされたactionは、一回の再レンダリングのみで済みます。

同一性の比較とアップデート

関数コンポーネントのレンダリングの際、引数に与えられたselector関数が呼ばれ、その結果がuseSelectorの返り値となります。(前回のコンポーネントのレンダリングと結果が同じ場合、キャッシュされた結果はselectorの再レンダリング無しで返されます。)

しかしながら、もし、selectorの結果が明らかに前の結果と異なる場合、Reduxのstoreにactionがdispatchされた時に、useSelectorは一回の再レンダリングのみを実施させます。Reduxバージョン7.1.0-alpha.5の時点では、デフォルトの比較は厳密比較(===)です。これは再レンダリングが必要かどうか決定する、mapState関数の結果を曖昧比較でチェックするconnect関数と異なる点です。これはuseSelectorを扱う上でいくつかの示唆を与えます。

mapStateでは全ての個々のフィールドは組み合わされたオブジェクトとして返されました。そこでは返されたオブジェクトが新しい参照をしたものかどうかは問題ではありませんでした。つまり、connect関数は単に個々のフィールドを比較しているだけです。useSelectorは、常に新しいオブジェクトを返すため、デフォルトでは毎回再レンダリングを強制します。もし、複数の値をstoreから取得したい場合は、以下のようにできます。

・useSelctorを複数回呼び出し、それぞれで単一のフィールドを返すようにする。
・1つのオブジェクトで保存されたselctor(memorized selector)を作るライブラリを使うようにする。しかし、1つの値が変更された場合は新しいオブジェクトを一つだけ返すようにする。
・同一性を比較する関数として、useSelctorの引数に、React ReduxのshallowEqual関数を使う。(以下例)

import { shallowEqual, useSelector } from 'react-redux'

// later
const selectedData = useSelector(selectorReturningObject, shallowEqual)

useSelectorの使用例

基本的な使い方

import React from 'react'
import { useSelector } from 'react-redux'

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter)
  return <div>{counter}</div>
}

抜き出したいものを決めるために、クロージャを使ってpropsを扱う

import React from 'react'
import { useSelector } from 'react-redux'

export const TodoListItem = props => {
  const todo = useSelector(state => state.todos[props.id])
  return <div>{todo.text}</div>
}

保存されたselector(memoizing selector)を使う

上記のようにインライン要素のselectorを扱う場合にuseSelectorを用いる際は、コンポーネントがレンダリングされる毎回で新しいselectorのインスタンスが作られます。これはselectorがなんのstateも持っていない場合の挙動です。しかしながら、例えばreselectライブラリのcreateSelector関数によって作られる、保存されたselector(memoizing selector)は内部にstateを持ちません。そして、それゆえに扱う際には注意が必要です。以下にmemoizing selectorの典型的な使用例を示します。

selectorがstateのみに依存しない場合、同じselectorのインスタンスがそれぞれのレンダリングで使われるようにするために、コンポーネントの外で宣言されることを確認してください。

import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'

const selectNumOfDoneTodos = createSelector(
  state => state.todos,
  todos => todos.filter(todo => todo.isDone).length
)

export const DoneTodosCounter = () => {
  const numOfDoneTodos = useSelector(selectNumOfDoneTodos)
  return <div>{numOfDoneTodos}</div>
}

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <DoneTodosCounter />
    </>
  )
}

selectorがコンポーネントのpropsに依存している場合も同様です。しかし、単一のコンポーネントで単一のインスタンスのみが使用可能なので注意してください。

import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'

const selectNumOfTodosWithIsDoneValue = createSelector(
  state => state.todos,
  (_, isDone) => isDone,
  (todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)

export const TodoCounterForIsDoneValue = ({ isDone }) => {
  const numOfTodosWithIsDoneValue = useSelector(state =>
    selectNumOfTodosWithIsDoneValue(state, isDone)
  )

  return <div>{numOfTodosWithIsDoneValue}</div>
}

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <TodoCounterForIsDoneValue isDone={true} />
    </>
  )
}

selectorが複数のコンポーネントのインスタンスで使われており、それらのコンポーネントのpropsに依存している場合、それぞれのコンポーネントのインスタンスがそれらのselectorのインスタンスを取得していることを確認してください。(なぜこれが必要なのかについてより詳細な説明は、ここを参照してください。)

import React, { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'

const makeNumOfTodosWithIsDoneSelector = () =>
  createSelector(
    state => state.todos,
    (_, isDone) => isDone,
    (todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
  )

export const TodoCounterForIsDoneValue = ({ isDone }) => {
  const selectNumOfTodosWithIsDone = useMemo(
    makeNumOfTodosWithIsDoneSelector,
    []
  )

  const numOfTodosWithIsDoneValue = useSelector(state =>
    selectNumOfTodosWithIsDone(state, isDone)
  )

  return <div>{numOfTodosWithIsDoneValue}</div>
}

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <TodoCounterForIsDoneValue isDone={true} />
      <span>Number of unfinished todos:</span>
      <TodoCounterForIsDoneValue isDone={false} />
    </>
  )
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Intersection Observerの使い方!時間差スクロールイベントのサンプル付き

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