20200518のReactに関する記事は10件です。

[deck.gl]React x TypeScript x DeckGL Part2

Nx(React)ワークスペースにdeck.glの環境構築を行った際の備忘録その2

前回の記事はこちら
[deck.gl]React x TypeScript x DeckGL Part1

事前準備

Mapboxにアカウント登録し、アクセストークンを取得する
https://account.mapbox.com/

トークンはgit管理下に置かないように注意

react-map-glを使って地図を表示する

react-map-glのhooksサンプルに沿ってシンプルな地図を表示する

  • MAPBOX_ACCESS_TOKENにはMapboxアカウントで取得したトークンを記述する
  • useStateに型パラメータが渡せるのでreact-map-glからimportしたMapboxProps型を渡した
my-map.tsx
import React, { useState } from 'react';
import ReactMapGL, { MapboxProps } from 'react-map-gl';

import './my-map.scss';

export const MyMap = () => {
  const MAP_STYLE = 'mapbox://styles/mapbox/streets-v10';
  const MAPBOX_ACCESS_TOKEN = 'pk.xxxxxxxxxxxxxx' // 取得したAPIトークン

  const [viewport, setViewport] = useState<MapboxProps>({
    width: '100vw',
    height: '100vw',
    latitude: 34.704048,
    longitude: 135.494883,
    zoom: 15,
  });

  return (
    <ReactMapGL
      {...viewport}
      mapStyle={MAP_STYLE}
      mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN}
      onViewportChange={setViewport}
    />
  );
};

export default MyMap;

表示結果

http://localhost:4200を開くと地図が表示される

スクリーンショット 2020-05-18 22.25.36.png

deck.glのレイヤーを表示する

deck.glを使ってレイヤーを使ったレイヤーの利用。
シンプルな例として地図上にScatterplotLayer(散布図)を表示する

変更点
* ベースの地図はStaticMapに変更し、アクセストークンとスタイルのみを定義する
* 緯度経度の情報はDeckGLのコンポーネントに渡す

my-map.tsx
import React from 'react';
import DeckGL from '@deck.gl/react';
import { ScatterplotLayer } from '@deck.gl/layers';
import { StaticMap } from 'react-map-gl';

import './my-map.scss';

export const MyMap = () => {
  const MAP_STYLE = 'mapbox://styles/mapbox/streets-v10';
  const MAPBOX_ACCESS_TOKEN = 'pk.xxxxxxxxxxxxxx' // 取得したAPIトークン
  const initialViewState = {
    longitude: 135.495734,
    latitude: 34.700559,
    zoom: 14,
  };

  const layers = [
    new ScatterplotLayer({
      id: 'example',
      data: [
        {
          name: '梅田',
          radius: 12000,
          coordinates: [135.495734, 34.700559],
        },
        {
          name: '淀屋橋',
          radius: 12000,
          coordinates: [135.501413, 34.692552],
        },
      ],
      stroked: false,
      filled: true,
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      getPosition: (d) => d.coordinates as any,
      getRadius: (d) => Math.sqrt(d.radius),
      getFillColor: [0,204,102],
    }),
  ];

  return (
    <DeckGL
      initialViewState={initialViewState}
      controller={true}
      layers={layers}
      effects={[]}
      width="100%"
      height="100%"
    >
      <StaticMap
        width="100%"
        height="100%"
        mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN}
        mapStyle={MAP_STYLE}
      />
    </DeckGL>
  );
};

export default MyMap;

表示結果

指定した緯度・経度に緑の円が表示された
スクリーンショット 2020-05-18 22.49.25.png

所感

  • 環境構築から導入までは比較的簡単
  • 実際に導入する際はDeckGLの初期値に渡すオブジェクトの型定義を丁寧に行いたい

参考資料

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

Reactについて

前提

Reactについて学んだことを書いていきます。

本題

Reactとは

React.js とは、UI のパーツを作るためのViewの機能を提供するライブラリ。
Facebook が OSS として公開している。

特徴

Reactには、JSXという記法がある。

JavaScriptの構文でDOM要素を操作しようとすると読むのが大変だが、JSXによってHTMLとJavaScriptが合体することでDOM要素の設計図を人間が読みやすくなる。

差分だけ表示するため早い

Reactは、何らかの理由でページが書き換わったことを自動的に検知して、差分だけを再表示してくれるため、表示がとても早い。

単方向データフロー

Reactは、基本的に親コンポーネントから子コンポーネントにデータが一方通行で渡っていく。
この一方通行という思想があるおかげで、DOM要素間でデータの不整合が出ないよう管理しやすくなる。

Reactインストール

まずは、nodeとnpxがインストールされているか確認。

terminal.
$ node -v
$ npx -v

Reactアプリ作成

terminal.
$ npx create-react-app アプリ名
...
...
success Uninstalled packages.
✨  Done in 4.61s.

Initialized a git repository.

Success! Created アプリ名 at /Users/username/Desktop/works/アプリ名
Inside that directory, you can run several commands:

  yarn start
    Starts the development server.

  yarn build
    Bundles the app into static files for production.

  yarn test
    Starts the test runner.

  yarn eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd アプリ名
  yarn start

Happy hacking!

これでインストール完了!
作成したアプリケーションに移動し、サーバーを立ち上げる!

terminal.
$ cd アプリ名
$ npm start

localhost:3000にアクセスしReactの画面が立ち上がれば完了!

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

[React]styled componentsで子要素にも適応する!

最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです!

styled componentsを使う!

Semantic UI Reactを使用中
Menu.Itemにも適応させる場合、どの様なコードになるのかちょっと分からなかったのでメモとして残す

index.js
import React from "react";

import { Menu } from "semantic-ui-react";

const MenuStyled = styled(Menu)`
  &&& {
    width: calc(100% - 20px);
    margin: 10px;
  }
`;

&&&この記事を見て理解した!

!!??ああ、トリッキーだ。&&& {}って何だ。
AAAというアンカーがあったとすれば、
AAAとして.myLinkでデザインしていくことになると思いますが、それと同じ振る舞いをします。
これはCSSの詳細度と闘っています。

const MenuStyled = styled(Menu)Menu.Itemにもcssを反映させたい場合は以下の方法でいけた!

index.js
import React from "react";

import { Menu } from "semantic-ui-react";

const MenuStyled = styled(Menu)`
  &&& {
    width: calc(100% - 20px);
    margin: 10px;
  }
  &&&.ui.menu .item {
    padding-top: 2rem;
    padding-bottom: 2rem;
    font-weight: 600;
  }
`;

参考にした記事

How do you use first-child or last-child selectors with SC?

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

華麗なるGatsby.jsの実践(型を変更してみよう)

GraphQLスキーマをカスタマイズする

先日、markdownのimgをgraphQLで管理しようとしたところ、型の類推エラーによってgraphQLのスキーマをいじらなければならなくなりました。なのでこのガイドを学習します!
ハイパー意訳がほぼほぼですのでご了承ください?‍♂️

graphQLのスキーマ言語をおおよそ知る必要がある

その前に、そもそも、graphQLに関する記述法を知らなかったので、そこをざっくり学習します。
https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000
私は上記で学習させていただきました!

特に必要となる知識は、スキーマ言語(Web APIの仕様を定義する方)の
fieldの定義方法と,Interfaceについてです。

type Query {
 currentUser: User!
}
interface Node {
 id: ID!
}

Interfaceは必要となるfieldのセットを束ねた抽象型です。

公式ガイド

では公式ガイドを見てみます。

このガイドは

  • プラグインの作成者

  • 自動型類推によって作成されたスキーマを修正したい方(私)

  • ビルドの最適化を測る方

  • スキーマに興味のある方

を対象としているようです。こちらかなり長いのですが、ここでは前編部分のみを扱います。
気になる方は続きを読むと良さそうです!

graphQLの強みは何と言っても多くの情報源を一括で扱えること!
そのためにはGraphQLスキーマを生成しなければなりません。

例 マークダウンとJSON

Markdownファイルからブログ記事を生成する。記事内容に加えて筆者情報をJSON形式で提供する。
時々ある寄稿者の情報は別のJSONとして保存する。

.md
---
title: Sample Post
publishedAt: 2019-04-01
author: jane@example.com
tags:
  - wow
---
# Heading
Text
author.json
[
  {
    "name": "Doe",
    "firstName": "Jane",
    "email": "jane@example.com",
    "joinedAt": "2018-01-01"
  }
]
contributor.json
[
  {
    "name": "Doe",
    "firstName": "Zoe",
    "email": "zoe@example.com",
    "receivedSwag": true
  }
]

これらをGraphQLでクエリするためにはgatsby-source-filesystem,gatsby-transformer-remark,gatsby-transformer-jsonを使用しますが、これらのプラグイン内部で何が行われているかというと、
markdownファイルをユニークなidとMarkdownRemarkタイプを持つnodeオブジェクトに変換しています。

同様に著者データはAuthorJsonタイプのnodeオブジェクトに変換され、
寄稿者データはContributorJson型のノードオブジェクトに変換されています。

スクリーンショット 2020-05-18 18.57.49.png

Node interface

Source pluginやtransformer pluginによって作られるGatsbyのGraphQLのスキーマです。
Id, parent,children,またinternal型などといったfieldがセットされています。

interface Node {
  id: ID!
  parent: Node!
  children: [Node!]!
  internal: Internal!
}

type Internal {
  type: String!
}

** TODO: internalって何でしょう..イマイチ調べてもわからずでした。

例 autor.json

例)Gatsby-transformer-jsonで作成するautor.json用のnode型は、以下のようになっています。

type AuthorJson implements Node {
  id: ID!
  parent: Node!
  children: [Node!]!
  internal: Internal!
  name: String
  firstName: String
  email: String
  joinedAt: Date
}

実際にgatsbyが作成したスキーマを確認するには、GraphQL Playgroundがオススメです。

プロジェクト上で

$ GATSBY_GRAPHQL_IDE=playground gatsby develop

そしてhttp://localhost:8000/___graphqlを開いて右画面側にある Schemeタブを開きます。
スクリーンショット 2020-05-18 18.03.52.png

思った以上にぎっしりしてます。

自動型推論

先ほどのautor.jsonを見てみましょう。

[
  {
    "name": "Doe",
    "firstName": "Jane",
    "email": "jane@example.com",
    "joinedAt": "2018-01-01"
  }
]

どこにも型定義などされていないですが、GraphQLではうまいこと動かせています。
これは型の類推を行なってくれているからですね。
1つ1つのfield内容を確認し、型をチェックすることで実現しています。

しかしこの型類推には2つの問題点があります。

  • 時間がかかり、負担がかかる

  • 型とデータが異なっていた場合、型の類推が失敗する

2つ目についてですが、例をあげます。

.md
  +  {
  +    "name": "Doe",
  +    "firstName": "John",
  +    "email": "john@example.com",
  +    "joinedAt": "201-04-02"
  +  }
  ]

joinedAt部分がスペルミスによって、Dateと解釈できなくなっています。

これらを解決するためには、型を明示的に示すことです。

## 型定義

createType actionを使って型を明示的に定義します。

gatsby-node.js
  exports.createSchemaCustomization = ({ actions }) => {
    const { createTypes } = actions
    const typeDefs = `
      type AuthorJson implements Node {
        joinedAt: Date
      }
    `
    createTypes(typeDefs)
  }

全ての型を定義する必要がない点に注意してください。(name,firstName等)

そもそも型推論をやらない!というストイックな方もいるでしょう。
これを行うことでパフォーマンスの向上が見込めます。先ほども開設したように、型の類推は負担が大きいため、大規模なプロジェクトほどパフォーマンス低下が著しくなります。
@dontInferディレクティブを用いて型推論をオプトアウトできます。

ディレクティブってなんだ?という方は先ほどのGraphQLスキーマについての解説をどうぞ!https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000)

gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    type AuthorJson implements Node @dontInfer {
      name: String!
      firstName: String!
      email: String!
      joinedAt: Date
    }
  `
  createTypes(typeDefs)
}

これがストイックなバージョンになります。
Node interfaceが提供するフィールドについては、Gatsby側で追加してくれるので明示しなくて大丈夫です。(id,parentなど)

ネストしている型

今まではString型やDate型などのスカラー型のみを扱ってきました。GraphQLでは他にも

  • ID型

  • Int型

  • Float型

  • Boolean型

  • JSON型

なども扱うことが可能です。また、複雑なオブジェクト値だって扱えます。

markdown-remarkを例にとってみます。

以下の記述によってfrontmatter.tagsは必ず文字の配列となります。

gatsby-node.js
  exports.createSchemaCustomization = ({ actions }) => {
    const { createTypes } = actions
    const typeDefs = `
      type MarkdownRemark implements Node {
        frontmatter: Frontmatter
      }
      type Frontmatter {
        tags: [String!]!
      }
    `
    createTypes(typeDefs)
  }

こちらの記述のように、直接Frontmatter型を指定すると失敗してしまいます。

gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    # これは失敗!!!
    type Frontmatter {
      tags: [String]!
    }
  `
  createTypes(typeDefs)
}

なぜなら、Frontmatter型がソースプラグインやトランフォーマープラグインによって作成されないため、Nodeインターフェースが実装されくなってしまうからです。

そのためにこのような処理となります。

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

SPAで外部スクリプトを動的にロードする方法

何をするか

SPAで外部から読み込むスクリプトのコード<script>~~~</sccript>をページによって切り替えれるかを調べたのでまとめます。

↓このような感じです。
ダウンロード.gif

react-headerでタイトルを変更する要領で動的にロードさせようとしましたが、発火させることができなかったため以下のような方法を取りました。
サイト全体で使う時はグローバルにおけばいいのですが、使用箇所が限定される場合はこのような使い方をしてみてもいいのかもと思います:open_hands:

構成

PageAとPageBがあり、PageBでのみ外部スクリプトを読み込むこととします。
(外部スクリプトはalert.jsというalertを出すだけのjsを用意しました)

.
// htmlにrender
├── index.js
└── modules
       //↓react-routerで出しわけ
    ├── App.js
    | // ↓PageAのコンポーネント(外部スクリプトの読み込み無し)
    ├── PageA.js
    | // ↓PageBのコンポーネント(外部スクリプトの読み込みあり)
    ├── PageB.js
    | // ↓別コンポーネントで読み込んだ外部スクリプトを削除する関数
    ├── clearScript.js
    | // ↓scriptタグを動的に作成する関数
    └── loadDynamicScript.js

App.js

react-routerで出しわけのみを記述しています。

app.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

import PageA from "./PageA";
import PageB from "./PageB";

// React-Routerのルーティングのみ
const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/" exact>
          <PageA />
        </Route>
        <Route path="/B" exact>
          <PageB />
        </Route>
      </Switch>
    </Router>
  );
};

export default App;


PageA.js

PageAのコンポーネント *外部スクリプトの読み込み無し

PageA.js
import React, { useEffect } from "react";
import { BrowserRouter as Router, useHistory } from "react-router-dom";

import { clearScript } from "./clearScript";

// 外部スクリプトの読み込み無し
const PageA = () => {
  const history = useHistory();

  // 別ページで外部スクリプトが読み込まれている場合削除する
  useEffect(() => {
    clearScript();
  }, []);

  return (
    <div>
      <p>this is PageA</p>
      <button onClick={() => history.push("/B")}>Next</button>
    </div>
  );
};

export default PageA;

PageB.js

PageBのコンポーネント *外部スクリプトの読み込みあり
今回はreact-hooksを使いましたが、componentWillMountで呼び出せばOKです!

PageB.js
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, useHistory } from "react-router-dom";

import { loadDynamicScript } from "./loadDynamicScript";

const PageB = () => {
  const [hasScript, setHasScript] = useState(false);

  // 外部スクリプトを動的に読み込む
  useEffect(() => {
    loadDynamicScript(() => {
      setHasScript(true);
    });
  }, [hasScript]);

  const history = useHistory();
  return (
    <div>
      {/* 外部スクリプトの読み込み完了後に表示 */}
      {hasScript ? (
        <div>
          <p>this is PageB</p>
          <button onClick={() => history.goBack()}>Go Back</button>
        </div>
      ) : (
        ""
      )}
    </div>
  );
};

export default PageB;

clearScript.js

別コンポーネントで読み込んだ外部スクリプトを削除する関数です。
PageAに遷移した場合PageBでappendされたスクリプトタグが残るためそちらを削除します。

clearScript.js
// 作成したscriptタグを削除する
export const clearScript = () => {
  const externalScript = document.getElementsByClassName("externalScript");

  if (externalScript) {
    console.log(externalScript);
    while (externalScript.length) {
      externalScript.item(0).remove();
    }
  }
};

loadDynamicScript.js

外部スクリプト(<script></script>タグ)を動的にを作成し、<body></body>の中に挿入する関数を作成します。

loadDynamicScript.js
export const loadDynamicScript = callback => {
  // スクリプトに付与するIDを指定
  const scriptId = "alert";
  const existingScript = document.getElementById(scriptId);

  // scriptががなければ新しく作成する
  if (!existingScript) {
    const script = document.createElement("script");
    script.id = scriptId;
    script.type = "text/javascript";
    // 外部スクリプトのURLはコチラに
    script.src = "./alert.js";
    // 削除するためのtargetとしてclassNameを付与
    script.className = "externalScript";
    // 作成したtagをappend
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }

  if (existingScript && callback) callback();
};

もっといい方法があるのでは??と思いながら書いていました。。
何か良き方法があればご教示頂きたいです:bow:

参考記事

https://cleverbeagle.com/blog/articles/tutorial-how-to-load-third-party-scripts-dynamically-in-javascript

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

rails初心者が好奇心でreactに手を出してみる

フロント側も触ってみたいとおもったので、react.jsで簡単なアプリを作っていくことにしたので、環境構築について備忘録も兼ねてアウトプットしておく。

(cloud9で開発を始めたのですが、バージョン指定等で若干ハマりました。)

node.jsをインストールするのあたってhomebrewが必要になるくさいので、インストール。

$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"

このままだとパスが通ってないと警告されるので、通します。

$ test -d ~/.linuxbrew && PATH="$HOME/.linuxbrew/bin:$HOME/.linuxbrew/sbin:$PATH"
$ test -d /home/linuxbrew/.linuxbrew && PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:$PATH"
$ test -r ~/.bash_profile && echo "export PATH='$(brew --prefix)/bin:$(brew --prefix)/sbin'":'"$PATH"' >>~/.bash_profile
$ echo "export PATH='$(brew --prefix)/bin:$(brew --prefix)/sbin'":'"$PATH"' >>~/.profile

これでOK。
パス通すって原理はわかるんだけど、コードの意味ちゃんと理解できてるかと聞かれるとアブないんだよな、調べとこう。

(参考:https://qiita.com/nasuvitz/items/5eec6ab9444cff8e9467)

homebrewがインストールできたら、node.jsをインストールしましょう。

$ brew install node

続いて、node.jsのバージョンを指定したいのですが、cloud9では標準で
nvm(Node Version Manager)がインストールされているようなので
(もしインストールされてなかったら、公式ドキュメントを参考に:https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/sample-nodejs.html)
こちらを利用してバージョン指定をします。

$ nvm install バージョン

これでOK。node -v と指定して

$ node -v
vバージョン

と表示されれば完了です。

次はJavaScriptのパッケージマネージャであるyarnをインストールします。Rubyでいうところのbundler(?)。

$ brew install yarn --ignore-dependencies

以前はオプションに --without-node というものを使用していたみたいですが、そちらを参照したところ「知らないオプションなのでシカトします!」ってバッサリいかれたので記述を変えました。
(参考:http://babababand.hatenablog.com/entry/2019/04/06/071534)
エラーメッセージってたまに面白いのあるからいいですよね。ふふってなる。

$ yarn --vesion
yarn install v1.22.4
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 0.12s.

みたいなのが返ってくればOK。

新しいディレクトリを作成して、webpack等をインストールします。

$ mkdir React_TO_DO
$ cd React_TO_DO
$ git init
$ yarn init
question name (React_TO_DO): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author: 
question license (MIT): 
question private: 

yarn initの後に出てくるものは、全部エンターでOKです。
次のコードでwebpack等をインストールします。

$ yarn add --dev webpack webpack-cli 

とりあえずこれで開発は進めていけそうかなぁ。

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

もう非同期処理に悩まない!非同期処理をシンプルに書けるフレームワークを作りました?

この記事は何ですか?

この記事は、筆者が作ったフレームワークの紹介記事で、フレームワークの使い方や有用性について解説しています。

タイトルに「 非同期処理をシンプルに書ける」と書いてありますが、それはあくまで筆者の主観ですので、本当にシンプルかはご自身の感覚で判断してください。また、「もう非同期処理に悩まない!」なんて書いてありますが、どのみち悩むのは言うまでもないでしょう。

※紹介するフレームワークはまだ開発中なので、バグが多く含まれている可能性があります。( 一応、一通り動くことは確認しましたが。 )

作ったモノ


trela link card image

非同期処理をシンプルに記述できる、Store管理フレームワークです。現在は、React.jsのみに対応しています。

npmのページ

https://www.npmjs.com/package/trela

インストール

npmでダウンロードできます。

npmでインストール
$> npm install trela

or

yarnでインストール
$> yarn add trela

使い方

基本的な使い方を3つほど紹介します。

シンプルな使い方

fetchUserという非同期関数を Trela を使って、実行して、その結果をコンポーネントに反映するサンプルコードです。

シンプルな例
import React from "react";
import { render } from "react-dom";
import { useTrela, TrelaProvider, createContextValue } from "trela";

const contextValue = createContextValue({
  initState: {
    user: {
      name: "まだ情報がありません",
      age: NaN,
    }
  },

  reducer: (state, action) => {
    switch(action.type) {

      // fetchUserの通信結果をStateに反映
      case "fetchUser":
        return { ...state, user: action.payload };

      default:
        return state; 
    }
  },

  apis: {
    fetchUser: async (user_id) => {
      const ref = await fetch("__YOUR_API_URL__?user_id=" + user_id);
      const user = await ref.json();

      return user; // actionのpayloadとしてreducerに渡される値
    }
  } 
});

const SampleComponent = () => {
  const { apis } = useTrela();
  const { fetchUser } = apis;

  // 非同期処理を実行するためのobjectを取得する
  const ref = fetchUser("ユーザーID");

  // fetchUserを実行する。通信が完了した時に、描画更新が入る
  const [user, isLoading] = ref.start(state => state.user);

  return (
    <>
      {isLoading ? <p>Now Loading ...</p> : null}      

      <h1>User</h1>
      <p>name : {user.name}</p>
      <p>age : {user.age || "まだ情報がありません"}</p>       

      {isLoading ? (
        <button onClick={() => ref.cancel()}>
          通信をキャンセル
        </button>
      ) : (
        <button onClick={() => ref.forceStart()}>
          ユーザー情報をリロード
        </button>
      )}
    </>
  );
}

const Root = () => (
  <TrelaProvider value={contextValue}>
    <SampleComponent  />
  </TrelaProvider>
);

render(<Root />, document.getElementById("root"));

色々と書いてありますが、やっていることは定義したfetchUserを実行して、コンポーネントにそれを反映しています。
本来であれば、useEffectuseStateを用いて非同期処理を書くはずですが、そこら辺の面倒くさい所は、Trelaがやってくれますし、TrelaがグローバルStateを持っているので、他のコンポーネントとの連携もできます。

直列処理

今度は、checkLoginfetchUserの二つの非同期関数を直列に実行するサンプルコードです。
checkLoginが終わった後にfetchUserを実行する形ですね。

直列処理の例
/* -- import部分はシンプルな例と同じため省略 -- */

const contextValue = createContextValue({
  initState: {
    isLogin: false,
    user: {
      name: "まだ情報がありません",
      age: NaN,
    }
  },

  reducer: (state, action) => {
    switch(action.type) {
      // checkLoginの通信結果をStateに反映
      case "checkLogin":
        return { ...state, isLogin: Boolean(action.payload) };

      // fetchUserの通信結果をStateに反映
      case "fetchUser":
        return { ...state, user: action.payload };

      default:
        return state; 
    }
  },

  apis: {
    checkLogin: async () => {
      const ref = await fetch("__YOUR_API_URL__");
      const { isLogin } = await ref.json();

      return isLogin; // actionのpayloadとしてreducerに渡される値
    },

    fetchUser: async (user_id) => {
      const ref = await fetch("__YOUR_API_URL__?user_id=" + user_id);
      const user = await ref.json();

      return user; // actionのpayloadとしてreducerに渡される値
    }
  } 
});

const SampleComponent = () => {
  const { steps, apis } = useTrela();
  const { checkLogin, fetchUser } = apis;

  // 直列実行する関数を定義。今回の例では、checkLogin -> fetchUser の順番で実行される
  const ref = steps([ checkLogin(), fetchUser("ユーザー") ]);

  // 配列内の非同期関数を直列に実行する
  // 全ての非同期処理が完了した時に、描画更新される
  const [state, isLoading] = ref.start(state => state);

  return (
    <>
      {isLoading ? <p>Now Loading ...</p> : null}      

      <p>{state.isLogin ? "ログインしています" : "ログインしていません"}</p>

      <h1>User</h1>
      <p>name : {state.user.name}</p>
      <p>age : {state.user.age || "まだ情報がありません"}</p>       

      {isLoading ? (
        <button onClick={() => ref.cancel()}>
          直列処理をキャンセル
        </button>
      ) : (
        <button onClick={() => ref.forceStart()}>
          直列処理をリロード
        </button>
      )}
    </>
  );
}

/* -- シンプルな例と同じため省略 -- */

上記の注意点としては、steps関数の配列内には非同期関数のref( 関数の返り値 )を渡す必要があります。

サンプルで使った、steps関数は配列内の非同期処理を直列で実行しますが、配列内には他のsteps関数のrefも入れることができます。
以下に例を示します。

ちょっと複雑なsteps関数の使用例
/* -- 省略 -- */

const SampleComponent = () => {
  const { steps, apis } = useTrela(); 
  const { a, b, c, d } = apis;

  // a -> b の直列処理
  const ab_ref = steps([ a(), b() ]); 

  // a -> b -> c -> d の順番で直列実行されます
  const all_ref = steps([ ab_ref, c(), d() ]); 

  /* -- 省略 -- */
};

/* -- 省略 -- */

上記はちょっと複雑ですが、一応上記のような事もできます。これは、並列処理のrefも入れることができるので、直列と並列の組み合わせなんかもできます。

並列処理

非同期処理の中でも結構めんどくさい並列処理ですが、Trelaだと簡単に実装できます。
基本的な使い方は、直列の時と同じです。

並列処理の例
/* -- 直列のコードと同じため省略 -- */

const SampleComponent = () => {
  const { all, apis } = useTrela();
  const { checkLogin, fetchUser } = apis;

  // 並列実行する関数を定義。checkLogin と fetchUser が同時に実行される
  const ref = all([ checkLogin(), fetchUser("ユーザー") ]);

  // 配列内の非同期関数を並列に実行する
  // 全ての非同期処理が完了した時に、描画更新される
  const [state, isLoading] = ref.start(state => state);

  return (
    <>
      {isLoading ? <p>Now Loading ...</p> : null}      

      <p>{state.isLogin ? "ログインしています" : "ログインしていません"}</p>

      <h1>User</h1>
      <p>name : {state.user.name}</p>
      <p>age : {state.user.age || "まだ情報がありません"}</p>       

      {isLoading ? (
        <button onClick={() => ref.cancel()}>
          処理をキャンセル
        </button>
      ) : (
        <button onClick={() => ref.forceStart()}>
          直列処理をリロード
        </button>
      )}
    </>
  );
}

/* -- 直列のコードと同じため省略 -- */

steps関数とほとんど同じなので、簡単ですね。

all関数に渡す配列内には、steps関数のrefも入れることが可能です。なので、直列と並列を組み合わせた複雑なフローも構築可能です。

直列と並列を組み合わせた例
/* -- 省略 -- */

const SampleComponent = () => {
  const { all, steps, apis } = useTrela();
  const { a, b, c, d } = apis;

  const ab_ref = steps([ a(), b(), ]); // 直列処理
  const cd_ref = all([ c(), d() ]); // 並列処理

  // 直列処理と並列処理をまとめる
  const all_ref = steps([ ab_ref, cd_ref ]);

  // a -> b -> [ c, d ] の順番で実行される 
  const [state, isLoading] = all_ref.start(state => state);

  /* -- 省略 -- */
}

/* -- 省略 -- */

このような直列と並列を組み合わせた処理は、async/awaitでやっても結構めんどくさい所なので、そこを簡単に出来るように工夫しています。

Trelaが解決する事

使い方が分かったところで、Trelaが何を解決するかを紹介したいと思います。

非同期処理をシンプルに書ける

Trelaは、非同期処理をシンプルに書けるようにするために作りましたので、他のライブラリーよりかは書きやすくなっていると思います。どのくらいシンプルかは、以下にTrelaを使わないで非同期処理を書いた場合と比較すれば明らかだと思います。

Trelaを使わないで非同期処理を書いた場合
import React, { useState, useEffect } from "react";
import { render } from "react-dom";

const checkLogin = async () => {
  const ref = await fetch("__YOUR_API_URL__");
  const { isLogin } = await ref.json();

  return isLogin;
} 

const fetchUser = async (user_id) => {
  const ref = await fetch("__YOUR_API_URL?user_id=" + user_id);
  const user = await ref.json();

  return user;
}

const SampleComponent = () => {
  const [state, setState] = useState({ 
    isLogin: false,
    user: { 
      name: "", 
      age: NaN 
    } 
  });

  useEffect(() => {
    (async () => {
      const isLogin = await checkLogin();
      const user = await fetchUser("ユーザーID");

      setState({ isLogin, user });
    })()
  }, []);


  /* -- 省略 -- */
}


render(<SampleComponent />, document.getElementById("root"));

上記のソースコードだと、処理をキャンセルしたり、処理結果を子コンポーネント以外のコンポーネントに伝える事が出来ていないことに注意しましょう。また、これで直列や並列の組み合わせをやるのは骨が折れると思います。

早期Fetch & キャッシュ

useEffectを使ってないので、コンポーネントのマウントを待たずに非同期処理を実行します。また、Trela内部で非同期処理をキャッシュしているので、まったく同じ処理が再度実行された時は、実行を破棄します。これにより、無駄な通信や処理を省き、描画更新も最小限に保ちます。

forceStart関数は、キャシュ関係なしに非同期処理を実行するので、注意してください。

const SampleComponent = () => {
  const { apis } = useTrela();
  const { fetchUser } = apis;

  fetchUser("1").start(state => state); // 非同期処理を開始
  fetchUser("1").start(state => state); // 既に実行されているので、処理を破棄
  fetchUser("2").start(state => state); // 引数の値が違うので、非同期処理を開始

  // fetchUser("1").forceStart(); <- この関数をコンポーネントのフィールドで実行すると無限ループします
}

依存関係に考慮した描画更新

Reduxなどを使ってグローバルStateを更新する時、描画更新が無駄に実行されることがあります。
これは、Reactなどの差分描画するライブラリを使っている場合は、特に気にする必要がない場合が多いですが、
無駄に実行されているのはちょっと良くないと思いますし、結構な頻度で重くなる時があるので、Trelaではなるべく最小限に描画更新するようにしています。

Trelaは、コンポーネントの構造を記憶しているので、どのコンポーネントを更新すべきかを判断できます。なので、ユーザーはそこまで気にしなくても勝手に最適化してくれます。が、今はまだ開発中なので、ちゃんと最適化出来てない所がある可能性あります。

以下に申し訳程度の画像載せるので、なんとなく伝わっていれば幸いです。

trela-update-flow.001.jpeg

これから来るConcurrent Modeに柔軟に備える事ができる( 予定 )

React.jsでは、Concurrent Modeという非同期処理をより扱いやすくするためのAPIが発表されました。
2020/05 時点では、まだ実験的な機能となっていますが、後々はリリースされることでしょう。
Trelaでは、来たるConcurrent Modeに備えるためにそれらに対応する予定です。※ 2020/05 時点ではまだ対応してません。

Concurrent Modeに備えると言っても、ほとんど実装は変わらないのでConcurrent Modeにすぐ切り替えたり、すぐ戻したりできます。

※ まだReact.js側で仕様が決定していない所は、未対応になります。

ConcurrentModeに対応した場合
// Suspenseを使うときでも、同じソースコード
const SampleComponent = () => {
  const { apis } = useTrela();
  const { fetchUser } = apis;
  const [state] = fetchUser("user_id").start(state => state);

  /* -- 省略 -- */
}

// Suspenseを使いたいときは専用のコンポーネントで、useTrelaを使った既存のコンポーネントをラップするだけ
const Root = () => (
  <TrelaSuspense fallback={<Spinner />}>
    <SampleComponent />
  </TrelaSuspense >
);

const Spinner = () => <div>Now Loading ...</div>;

今後実装予定のモノ

今開発中の機能を紹介します。

テストツールを完備する

jestmochaで、e2eテストをすると思いますが、それをやりやすくするためのコンポーネントや関数を開発中です。
これにより、テスタビリティを損なわずに処理が書けると思います。

sample.spec.jsx
// 開発中のモノなので、ここに書いてあることは変更される可能性があります。

import { render, wait } from "your-testing-library";
import { TrelaProvider, createMockContext } from "trela";
import { SampleComponent } from "./src/SampleComponent";

describe("サンプルテスト", () => {
  test("fetchUserが呼ばれたかテストする", async () => {
    const mockValue = createMockContext({ /* ... */ });

    render(
      <TrelaProvider value={mockValue}>
        <SampleComponent />
      </TrelaProvider>
    );

    await wait(() => expect(mockValue.apis.fetchUser.complite).toBeTruthy());
  }); 
});

後書き&雑多な感想

ここまで読んでくれてありがとうございます!まだまだ未完成ですが、一応紹介してみました。
使い方はシンプルにしたつもりですが、まだまだ煮詰まってない感が否めない感じがしています。もっと改良せねば!
また、ReactのConcurrent Modeは思想はいいのですが、現状だとそこまで使うメリットが無いように感じます。使いこなすのが難しいですし、親コンポーネントに責任を丸投げしているのが、私はちょっと好きじゃないですね。だからと言って、無視するわけにもいかないので、Trelaで間を取っていけたらと思っています。

また、今回のフレームワークは個人開発をしているときに非同期処理がとても面倒くさく感じたので作ってみましたが、
おかげで、作っているサービスの進行が3か月も止まってしまい、挙句の果てには、仕事が一切無くなってしまったのですが、Trelaがある程度まで作れたので、良しとしておきましょう。

また何かのフレームワークなりライブラリなりを作ると思うので、その時にまた記事を書こうと思います。

それでは?

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

React.ts の状態管理に useContext を使う

Reactの状態管理の方法で迷ったので、
選択肢とその特徴をまとめようと思います。

Reactの状態管理の主な方法

  • useState
  • useContext :round_pushpin:
  • Redux

今回は、useContextを使った状態管理の方法についてまとめていきます。

useContext を使った状態管理のポイント

  • React.createContextで生成したContextをStoreとして管理する。
  • StoreにはReact.useReducerで生成した、StateDispatchを格納する。
  • ContentのProvider Componentで囲み、Scopeを決定する。
  • Storeを利用する際は、React.useContextで取得する。
  • Storeに格納したStateから状態を読み取る。
  • Storeに格納したDispatchから状態を変更する。

useContext を使った状態管理の例

store

  • React.createContextで生成したContextをStoreとして管理する。
  • StoreにはReact.useReducerで生成した、StateDispatchを格納する。
store.tsx
import * as React from 'react';

/**
 * Itemの型定義
 */
export interface Item
  extends Readonly<{
    name: string;
  }> {}

/**
 * Stateの型定義
 */
export interface State
  extends Readonly<{
    list: Item[];
  }> {}

/**
 * アクションの型定義
 */
export interface Action
  extends Readonly<{
    type: 'SAVE' | 'DELETE';
    payload: Item;
  }> {}

type Reducer = React.Reducer<State, Action>;

export type Dispatch = React.Dispatch<Action>;

/**
 * ストアの型定義
 */
export interface Store
  extends Readonly<{
    /*
     * State
     */
    state: State;
    /**
     * Dispatch
     */
    dispatch: Dispatch;
  }> {}

/**
 * Propsの型定義
 */
interface Props
  extends Readonly<{
    /*
     * Child Elements
     */
    children: React.ReactNode;
  }> {}

/**
 * Default Store
 */
const defaultStore: Store = {
  state: {
    list: [],
  },
  dispatch: (action: Action): void => {},
};

/**
 * Reducer
 */
const reducer: Reducer = (prevState: State, action: Action): State => {
  // 各処理は別ファイルに分割してもよい。
  switch (action.type) {
    case 'SAVE':
      // (ry
      return {...prevState};
    // break;
    case 'DELETE':
      // (ry
      return {...prevState};
    // break;
    default:
      throw new TypeError(`Illegal type of action: ${action.type}`);
    // break;
  }
};

/**
 * Context
 */
export const context: React.Context<Store> = React.createContext<Store>(
  defaultStore
);

/**
 * Provider Component
 */
export const Provider: React.FC<Props> = (props: Props): JSX.Element => {
  const [state, dispatch]: [State, Dispatch] = React.useReducer<Reducer>(
    reducer,
    defaultStore.state
  );

  return (
    <>
      <context.Provider value={{state, dispatch}}>
        {props.children}
      </context.Provider>
    </>
  );
};

Page Component

  • ContentのProvider Componentで囲み、Scopeを決定する。
Page.tsx
import * as React from 'react';
import List from './List';
import {Provider} from './store';

/**
 * Propsの型定義
 */
interface Props
  extends Readonly<{
    /*
     * Child Elements
     */
    children?: never;
  }> {}

/**
 * Page Component
 */
const Page: React.FC<Props> = (props: Props): JSX.Element => {
  return (
    <Provider>
      <List />
    </Provider>
  );
};

export default Page;

List Component

  • Storeを利用する際は、React.useContextで取得する。
  • Storeに格納したStateから状態を読み取る。
List.tsx
import * as React from 'react';
import ListItem from './ListItem';
import type {Item, State, Store} from './store';
import {context} from './store';

/**
 * Propsの型定義
 */
interface Props
  extends Readonly<{
    /*
     * Child Elements
     */
    children?: never;
  }> {}

/**
 * List Component
 */
const List: React.FC<Props> = (props: Props): JSX.Element => {
  // StoreからStateを取り出す。
  const {state}: {state: State} = React.useContext<Store>(context);

  return (
    <>
      <ul>
        {state.list.map(
          (item: Item, index: number): JSX.Element => {
            return <ListItem key={index} item={item} />;
          }
        )}
      </ul>
    </>
  );
};

export default List;

List Item Component

  • Storeを利用する際は、React.useContextで取得する。
  • Storeに格納したDispatchから状態を変更する。
ListItem.tsx
import * as React from 'react';
import type {Dispatch, Item, Store, Action} from '../../store';
import {context} from '../../store';

/**
 * Propsの型定義
 */
interface Props
  extends Readonly<{
    /*
     * Child Elements
     */
    children?: never;
    /**
     * Item
     */
    item: Item;
  }> {}

/**
 * List Item Component
 */
const ListItem: React.FC<Props> = (props: Props): JSX.Element => {
  // StoreからDispatchを取り出す。
  const {dispatch}: {dispatch: Dispatch} = React.useContext<Store>(context);

  // Dispatchに渡すActionを定義しておく。
  const saveAction: Action = {
    type: 'SAVE',
    payload: props.item,
  };

  return (
    <li>
      {props.item.name}
      <button
        onClick={(
          event: React.MouseEvent<HTMLButtonElement, MouseEvent>
        ): void => {
          dispatch(saveAction);
        }}
      >
        SAVE
      </button>
    </li>
  );
};

export default ListItem;

まとめ

  • メリット
    • React以外のモジュールに依存しない。
    • Providerを置く場所によってGlobalや任意の範囲のスコープに絞ることができる:mag:
    • prop drilling が少なくてすむ。
  • デメリット
    • Scopeを広くしすぎると、どこから変更されているか追いづらくなる
    • 非同期処理には対応出来ていないため、自前でなんとかしないといけない:innocent:
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gataby リンク トランジションについて (メモ)

gatsby において、ページ遷移をする設定の仕方についてです.

詳しいことは、公式ページにて載っています.

簡単なフェードトランジションです.

Layout.js
import { TransitionState } from "gatsby-plugin-transition-link"
import posed from 'react-pose'

export const Fade = posed.div({
    hidden: { opacity: 0 },
    visible: { opacity: 1 },
  })

  <TransitionState>
                { ({mount}) => {

                return(
                <Fade
                    pose={
                        mount
                        ? 'visible'
                        : 'hidden'
                    }>
                     <main>{children} </main>
                </Fade>
                )} }
           </TransitionState>

こちらによると、mountという値が、遷移ページペアのマウント状態を表すようです.

気になったのは length というオプションです.

exit={{
  length: 2
}}
entry={{
  length: 1
}}

こちら
length option によると、遷移したあとにトランジションリンクを押しても遷移しない時間の長さです.
遷移した後にアニメーションなどがかかる場合に有効かと思われます.

以上、参考になればなと思います !!

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

Reactのイベント処理を理解しよう

目次

概要

この記事は、Reactにおけるイベント処理の仕組みなどについてまとめています。主にReact初心者向けの内容となっています。
また私自身の備忘録としてイベントハンドラについても一覧としてまとめていますので、参考にしてみてください。

イベント処理とは

イベント処理とは、ユーザが「クリック」や「入力」などの操作が発生したときにプログラム側が行う処理のことを言います。

Reactのイベント処理

Reactでのイベント処理はDOM要素のイベント処理と非常に似ていますが、いくつかの特徴があります。

  • イベントはcamelCaseで名付ける(「onclick」ではなく「onClick」)
  • イベントハンドラには文字列ではなく関数を渡す

簡単な例を見てみましょう。特に<button>タブの中身に注目して見てください。
HTMLの場合

<html>
  <script>
    const buttonAlert = () => {
      alert('Clicked!');
    }
  </script>
  <body>
    <button onclick="buttonAlert()">Click me</button>
  </body>
</html>

Reactの場合

cnost App = () => {
  const buttonAlert = () => {
    alert('Clicked!');
  }

  return(
    <button onClick={buttonAlert}>Click me</button>
  );
}

<button>タブの中身がHTMLの場合とReactの場合とではわずかに違いますよね。
ReactでHTMLの場合のように記述してしまうとエラーとなりますので気をつけましょう。

ReactはJavaScriptのライブラリなので、イベント処理ではaddEventListenerを定義し、onClickなどのイベント属性内に記述することは変なのではと疑問に感じる方もいるかもしれません。しかしReact(JSX)では、イベント処理をonClick等のイベント属性に記述します。なぜこのような記述方法が可能なのかですが、JSXがレンダリングされるとき実際にはaddEventListenerに置換されるそうです。

上記の例のように状態などが無く、必ず同じ結果を返す場合は簡単に表現できます。
しかし状態によって処理に変更が生じる場合はどうなるでしょうか。

メソッドのbind

はじめに例を見てみましょう。

src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // ここに注目
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;

これはボタンが押されるたびにONとOFFを切り替える機能を実装したプログラムです。
このとき、handleClick()メソッド内ではthisを使ってstateを参照していますが、thisconstructor内で使用されているのでhandleClick()メソッド内でも使用できるように関連付けてあげる必要があります。
このようにconstructor内でhandleClick()メソッドをbindすることでthisを使用できるようになります。

ただメソッドが多くなればなるほど、this.メソッド = this.メソッド.bind(this)のような宣言も増えますよね。それを回避するため、つまり明示的にbindを記述しない記述方法があります。

方法その1:ES6のアロー関数をJSX内で記述する方法
方法その2:Class Fields利用する方法

それぞれ見てみましょう。
方法その1

src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    // bindを書かなくて良い
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>  {/* ここに注目 */}
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;

方法その2

import React, { Component } from 'react';

class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    // bindを書かなくて良い
  }

  handleClick = () => {  // ここに注目
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;

bindのある場合、方法その1、その2いずれの場合も同じ結果になります。bindのない場合の方がすっきり見えますし、記述量も少なくて済むので良いですね。

方法その1、その2のような記述ができる理由については、現時点で調べてもよくわからなかったので詳しい方がいればコメントなどで教えてもらえるとありがたいです。私のようによくわからないという人は、そういう仕様と捉えておけば大丈夫かとおもいます。

注意点として方法その2の手法はTC39というJavaScript関係の委員会が提案している手法なのですが、現在Stage3で実験的に行っているそうなので環境的にできない場合もあるかと思います。Create React Appではデフォルトで利用できます。

(補足)
方法その2のようにClass Fieldsを利用すると、クラス構文でconstructorの宣言も省略することができます。

src/Toggle.js
import React, { Component } from 'react';

class Toggle extends Component {
  // ここに注目(this.state → state)
  state = {isToggleOn: true};

  handleClick = () => {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

export default Toggle;

この場合propsはどう使うのか疑問に思うかもしれませんが、何も宣言せずに必要に応じてthis.propsも利用できます。

イベントハンドラ一覧

Reactで用いることのできるイベントハンドラはJavaScriptのイベントハンドラに準拠しています。ではどのようなイベントハンドラがあるか見てみましょう。

イベントハンドラ 説明
onBlur / onFocus フォーカスが移動したとき
onChange フォームの入力値 / 選択が変更されたとき
onSelect / onSelectStart テキストが選択されたとき
onSubmit / onReset フォームの送信時 / リセット時
onAbort / onError 画像読み込み時の中断 / エラー
onLoad / onUnload ページの読み込み時 / 切り替え時
onClick / onDblClick クリック / ダブルクリックされたとき
onKeyPress / onKeyDown / onKeyUp キーの状態に応じる
onMouseOver / onMouseOut マウスを乗せたとき / 離したとき
onMouseDown / onMouseUp マウスでクリックしたとき
onMousemove マウスを動かしているとき

この辺りは覚えるというよりは、使いたいときに参照する程度にしておけばいいと思います。他にも色々あるので必要に応じて調べて使うようにしましょう。

まとめ

今回はReactにおけるイベント処理とそのイベントハンドラについてまとめてみました。特に記述方法などの基礎的な部分に関してはしっかり抑えておきましょう。

  • イベントはcamelCaseで名付ける(「onclick」ではなく「onClick」)
  • イベントハンドラには文字列ではなく関数を渡す
  • bindは記述方法によって省略可能

参考資料

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