20200830のReactに関する記事は11件です。

Material-uiでDate pickerを使うときの注意点

本記事の目的

ReactのCSSフレームワーク「Material-ui」で日時入力(Date/Time)をするためのフォームを実装する際に詰まったので、メモ。

Material-ui公式ドキュメント内の該当項目はこちら↓
https://material-ui.com/components/pickers/

1. 必要なコンポーネント

公式ドキュメントの記載が不親切で、初心者には何が必要かいまいち分からなかった。
下記3つのコンポーネントの導入で動作しました。

"date-fns": "^2.16.0",
"@date-io/date-fns": "1.x"
"@material-ui/pickers": "^3.2.10"

※2020年8月時点のヴァージョン
注:ヴァージョン制限に注意、下記2.で解説

date-fns以外にも、moment,luxion,dayjsがあるけど、
詳しくないのでMaterial-uiの公式ドキュメント内で採用されている、date-fnsを採用。
※比較記事見つけたら追記します。

2. @date-ioのヴァージョン制限

これも公式ドキュメントをしっかり読めという話だけど、
https://material-ui-pickers.dev/
の中に、

Important: For material-ui-pickers v3 use v1.x version of @date-io adapters.
※意訳:v3のmaterial-ui-pickersnにはv1.X台の@date-ioを使いなさい。

とある。これを見逃してエラーにハマったので大事。

実装例

DatePickerコンポーネントの実装例を記載します。
useStateを上位から渡していますが、もっと良い方法があれば、ご指摘いただける嬉しいです。

import React from 'react';
import {
  MuiPickersUtilsProvider,
  KeyboardDatePicker,
} from '@material-ui/pickers';
import DateFnsUtils from '@date-io/date-fns';

const DatePicker = ({ selectedDate, setSelectedDate }) => {
  const handleDateChange = (date) => {
    setSelectedDate(date);
  };

  return (
    <MuiPickersUtilsProvider utils={DateFnsUtils}>
      <KeyboardDatePicker
        disableToolbar
        variant="inline"
        format="yyyy/MM/dd"
        value={selectedDate}
        onChange={handleDateChange}
      />
    </MuiPickersUtilsProvider>
  );
};

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

【React】なぜsuper(props)は必要なの?

 元(?)記事

https://qiita.com/hand-dot/items/61a4b808f110b12e4281

とても参考になったのですが、もう少し噛み砕いてわかりやすく解説していきます?‍♂️

 【疑問その1】なぜ、super()を呼び出すのか

結論から言うとthis.nameのようなthisを伴う初期化を親クラスで行うため。下記はReactではないJSのクラスです。

class Parent {
  constructor(name) {
    //ここで初期化している
    this.name = name;
  }
}

class Child extends Parent {
  constructor(name) {
    console.log(this.name); // エラーになる
    super(name);
  }
}

const test = new Child("佐藤");

直感的にthis.nameが使えそうに感じるが、初期化(親クラスのconstructorを実行)してないので、使えない。Reactの場合も同様。

// React.componentの内部
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}

class Child extends React.component {
  constructor(props) {
    console.log(this.props); // エラーになる
    super(props);
  }
}

 propsを渡す必要性

結論、親と自分のコンストラクターの実行が終わるまで、this.propsは未定義だから。

実は、React内部でpropsがインスタンスに割り当てられている。

// React内部
const instance = new YourComponent(props);
instance.props = props;

しかし、これはコンストラクター実行後に行われる処理なので、コンストラクター内でthis.propsを使った処理を書けなくなってしまう。

以上の理由から、super(props)を書きましょう。

何か間違っているところがありましたらご指摘よろしくお願いします?‍♂️

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

【React】stateについて学ぶ

 stateとは

  • コンポーネントが持つ状態のこと。
  • クラスコンポーネントで使用される。(hooksは一旦置いておく。)
  • コンストラクタ内で初期値が決められる。
  • setStateで更新する。
  • 値が変更されるとrenderされる。
import React from "react";

class Test extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "佐藤",
    };
  }

  render() {
    return (
      <>
        <h1>{this.state.name}</h1>
      </>
    );
  }
}

export default Test;

次にstateの更新について。以下のように書かなくてはいけない。

this.state({
  name = "山田"
})

以下のように書くのはNG。再レンダリングされないから。
jsx
this.state.name = "山田"

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

react で yarn startができなくなって困った

急にyarn startができなくて困ったので原因と経緯を書いておきます。
結果を先に言うとreact-scriptsを消してしまっていました…

発生した事象

$ yarn start
yarn run v1.22.5
$ react-scripts start
/bin/sh: react-scripts: command not found
error Command failed with exit code 127.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

発生の経緯

久々に触ったアプリにて、
package installひとつづつやるのめんどくさいから、package.json一気に書き換えて…
よしyarn installして、とりあえずエラー出てないか起動してみよう....あれ?そもそも起動ができない…なぜだ…

解決までの道筋

初めてみたエラーだ…なんだこれ…パニック…
とかなる前にまずはエラーメッセージを出してくれているのでちゃんと読む。

/bin/sh: react-scripts: command not found

yarn曰く、原因はreact-scriptsがないですよ?と言っている。
あ…なるほど…

これはどういうことか

$ yarn start

をした時に見ているのは

package.json
(...省略)
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
(省略...)

この部分。
ここでreact-scripts呼んでいるのにそんなもの見つかりませんよ?正気ですか?と言われている。

なるほど?ではreact-scriptsnode_modules/にいるはず(と思っている)ので調べる。

$ ls node_modules/ | grep react-scripts
$ 

あれ…ない?

$ ls -1 node_modules/ | grep react-
babel-plugin-add-react-displayname
babel-plugin-react-docgen
babel-plugin-transform-react-remove-prop-types
babel-preset-react-app
connected-react-router
create-react-context
css-to-react-native
enzyme-adapter-react-16
eslint-config-react-app
eslint-plugin-react-hooks
hoist-non-react-statics
mini-create-react-context
react-addons-create-fragment
react-app-polyfill
react-async-script
react-circle
react-clientside-effect
react-color
react-dev-utils
react-div-100vh
react-docgen
react-dom
react-draggable
react-dropzone
react-element-to-jsx-string
react-error-overlay
react-fast-compare
react-focus-lock
react-ga
react-google-recaptcha
react-gtm-module
react-helmet
react-helmet-async
react-hot-loader
react-hotkeys
react-infinite-scroller
react-input-autosize
react-inspector
react-is
react-lifecycles-compat
react-move
react-native-get-random-values
react-popper
react-popper-tooltip
react-pose
react-redux
react-router
react-router-dom
react-router-hash-link
react-scroll
react-select
react-side-effect
react-sizeme
react-syntax-highlighter
react-test-renderer
react-textarea-autosize
react-transition-group
react-use
storybook-react-router

やっぱりない…

package.jsonを見に行くと…react-scriptsが消えている…

解決

ということでpackage.jsonreact-scriptsを追記して、yarn-installして解決。

まとめ

ちゃんとエラーメッセージで教えてくれているので、
初めてみるエラーだからってパニックになってはいけない。

…あとエラーハンドリングする時は、
エラー対応した人がわかるようにエラーメッセンジャー作るの重要ですね。
気をつけます。

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

Firebase + redux-toolkit の学習備忘録

まず

本記事がqiita初投稿となります。
暖かい目でご覧いただければ幸いです。
※現在、追記&学習中です

ここまでの経歴

  1. 自作でReactとReact Nativeでアプリケーションを作成しようと思った
  2. AWSのEC2でやってみよう
  3. なんか手作り感がすごい、、カッコよく手軽に作りたかった→そうだFirebaseを使おう!
  4. Redux嫌いだから、useContextで乗り切ろう
  5. useContextも意外と好きじゃないかも。。。
  6. redux-toolkitっていうのがあるらしい、試してみよう
  7. redux-toolkitマジで最強!! 使っていこう
  8. Firebase + redux-toolkit でどんなアプリもさくっと作れそうなテンプレ覚えてみよう ←今ここ

困ったこと

firebaseUIの機能だけ継承して独自UIにしたいのにできない。。。

できない。

独自UI使いたいならfirebaseUIは使わない or DOM非表示にしてクリックイベント発火させるしかない。。。

firebaseuiのuiConfig.callbacks.signInSuccessWithAuthResult内でdispatch実行してもstoreが更新されない。。。

react-redux-firebase を使ってなんとか解決

Firebase + redux-toolkitの日本語ドキュメントなさすぎ、公式もredux-toolkitに対応してなさすぎ問題

もしかしてと思って調べてみたら、そもそもFirebaseを使ってる日本のサービスがあんまり見つからなかった。
意外と流行っていないのかな

個人開発ではとっかかりやすいから有名だけど、会社として開発するとなるとFirebaseは選択しないのかも。。。

redux-toolkitの非同期がなんかうまくできない

createAsyncThunkの返り値が action.payload に入るというルールを知らなかった

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

styled-componentsをTypescriptで使う

styled-components ✖️ Typescript

Typescriptでstyled-componentsを書く時に情報が少なかったので、いくつか

型定義

propsの型を定義するときは、次のようにジェネリクスで指定する

interface Props {
  color: string
}
const Sample = styled.div<Props>`
  color: ${(props) => {
    return props.color
  }}
`

例えば、buttonを属性ごとに色を変えたいときは

const Button = styled.button<{primary?: boolean}>`
  background: transparent;
  color: black;
  border: 2px solid black;
  width: 10em;
  ${props =>
    props.primary &&
    css`
      border: 2px solid skyblue;
    `}
`;
<Button>not primary</Button>
<Button primary>primary</Button>

スクリーンショット 2020-08-30 15.04.01.png

条件分岐

また、if文を使ってスタイルを適用することも可能。
例えば、typeというpropsを適用して色を変化させるときは、こんな感じ。

interface Props {
  type: 'SUCCESS' | 'FAIL';
}

const Flash = styled.div<Props>`
  text-align: center;
  margin: 5em;
  color: black;
  width: 10em;
  opacity: 0.8;
  background-color: ${props => {
    switch (props.type) {
      case 'SUCCESS':
        return 'rgba(32, 153, 134, 0.9)';
      case 'FAIL':
        return 'rgba(230, 65, 30, 1)';
      default:
        return 'rgba(32, 153, 134, 0.9)';
    }
  }};
`;

const Wrapper = styled.div`
  display: flex;
  align-items: center;
`;

const Sample: FC = () => {
  return (
    <Wrapper>
      <Flash type="SUCCESS">success</Flash>
      <Flash type="FAIL">fail</Flash>
    </Wrapper>
  );
};

スクリーンショット 2020-08-30 15.28.37.png

既存のcomponentを上書きしたいとき

typescriptとは関係ないですが、例えば基本の形はsemantic-uiなどのコンポーネントを使用したいが、色だけ変えたい時

import {Button} from 'semantic-ui-react';
const StyledButton = styled(Button)`
  margin: 20px !important;
  background: skyblue !important;
`;
<StyledButton>styled button</StyledButton>

スクリーンショット 2020-08-30 15.37.55.png

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

「Vercel + freenom + Getform.io」構成で「爆速 + 管理費完全無料」のポートフォリオをGatsbyJSで作ったら幸福度が高まった話

こんにちは(๑╹ω╹๑ )

つい先月のエントリでGatsbyJSの魅力をお伝えしたばかりですが、
今日はそのGatsbyJSで開発したポートフォリオサイトの構成を紹介します?

↓ 開発したポートフォリオサイトです

huuya.ga.png

今回のエントリとは関係ないですが、
技術構成は下記のとおりです?

  • React
  • GatsbyJS
  • Less

ポートフォリオサイトの構成について☺️

管理費完全無料は本当??

本当です?‍?

  • SPAサイトの静的ホスティング
  • 独自ドメイン
  • お問い合わせ管理

全ての管理費が無料です✨
※ 2020年8月30日現在
※ 詳細は以降に説明

静的ホスティングサービスのVercelって何が無料??

Vercel

  • 満載のDevOps
    • Git Integrationsで自動ビルド
      • GitHub(プライベートリポジトリ可
      • GitLab
      • Bitbucket
    • Integrations Marketplaceの利用も無料
      • VercelイベントをSlackに通知する
      • ビルド後にLightHouseで自動レポーティング
  • Starterから簡単にプロジェクトを開始できる
    • Gatsby.js
    • Nuxt.js
    • Angular
    • Next.js
    • Vue.js
  • カスタムドメイン(50個まで
  • 1ヶ月あたり100GBまでの転送量
  • 自動でSSL化
    • デプロイしたら自動でhttpsになっていた?
  • Serverless FunctionsでAPIの実装
    • 所謂FaaS

↑全てが無料です?
※ 個人利用プランの場合

Vercelの料金ページ

freenomで独自ドメインを無料で取得・更新する

freenom

gaという独自ドメインを無料で取得できました?

(個人開発ではいつも利用しています笑

正確には下記の制限があります?

  • .tk.ml.ga.cf.gqドメインが対象
  • 1〜12ヶ月の範囲でないと登録・更新できない(でも永久無料
  • ネームサーバーの登録 or URL転送のみ

freenomの料金ページ

Getform.ioでお問い合わせの管理

Getform.io

お問い合わせフォームのエンドポイントになって、
それを管理できるサービスです?

フォームを作成するたびにエンドポイントを発行してくれます!

個人利用の無料プランでは下記が無料です?

  • 1フォーム
  • 一ヶ月あたり100件のお問い合わせ
  • 単一ファイルのアップロード
  • 100MBのファイルストレージ
  • デフォルトのThanksページ
  • チームメンバーを1人招待する
  • スパム保護
  • Zapierとの統合
  • Getform API

Getform.ioの料金ページ

まとめ

個人のポートフォリオサイト規模の利用であれば、
不便なく、管理費も考えること無く利用できました?

制作過程でGlitchという、静的ホスティングとペアプログラミングでコラボレーションが出来るサービスを見つけました!

  • RAM:512MB
  • ディレスク容量:200MB

の範囲であれば無料で利用できるみたいです?

Glitchの料金ページ

近い内に少し触ってみようと思います?

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

【React】ミュータブルな操作とイミュータブルな操作

はじめに

業務で本格的にReactを触り始めて10ヶ月程経過しました。

実装を進める中でドキュメントを読んだり、ググったりする中で必ず目にするのが

  • 「Reactはイミュータブルな操作を要請する」
  • 「Reactでは破壊的な変更を伴う操作を行ってはいけない]

といった内容のものです。

なんとなくの理解で通り過ぎてしまっていたのですが、
そのせいでコンポーネントが期待どおり再レンダリングされず、画面が変化しないという現象に何度かぶち当たりました。
10ヶ月も基本的なことをスルーしていたので、調べて理解したことをまとめてみました。

ミュータブル/イミュータブルとは

まずは言葉の意味から

  • mutable (ミュータブル): 可変
  • immutable (イミュータブル): 不変

ミュータブルな操作とは
元の状態の変更を伴う(可変)操作

イミュータブルな操作とは
元の状態の変更を伴わない(不変)操作

言葉だけだとつまりどう言うこと??となるので実際の配列操作をしてみます。

ミュータブルな操作/ミュータブルな操作

配列への要素の追加をしてみます。

const arr = ['1', '2', '3'];
// ミュータブルな操作(イミュータブルではない操作)
arr.push('4');
console.log(arr);
// [ '1', '2', '3', '4' ];

// イミュータブルな操作(ミュータブルではない操作)
const immutableArr = [...arr, '5'];
console.log(immutableArr);
// [ '1', '2', '3', '4', '5' ]

実行結果だけを見てもパッと見違いがありません。
配列の操作で4と5という要素を加えた新しい配列が返ってきてるように見えます。

ここで注目するのが元の配列と新しい配列のデータの参照先です。
arr.push('4');
とした時にはコピー元のconst arr = ['1', '2', '3'];
arr.push('4');をした新しい配列も
arr = [ '1', '2', '3', '4' ];という情報を参照します。
arr = ['1', '2', '3'];`
の一つの情報(メモリ)に直接変更が加わり参照されるようになります。

乱暴な言い方ですが、元のarr = ['1', '2', '3'];の情報はなくなります。

const immutableArr = [...arr, '5'];
とした場合には
コピー元の配列([ '1', '2', '3', '4' ])
新しい配列(immutableArr)の参照先の情報をそれぞれ別々に持ちます。
arrは[ '1', '2', '3', '4' ]という情報を参照。
immutableArrは[ '1', '2', '3', '4', '5' ]という情報を参照。

※言語は異なりますが、下記サイトの図解がわかりやすかったです。
https://kurochan-note.hatenablog.jp/entry/20110316/1300267023

下記のようなオブジェクトの操作でも同様のことが行われます。

const obj = {name: 'a', type: 'old'};
// ミュータブルな操作(イミュータブルではない操作)
obj.type = 'new';
console.log(obj);
// { name: 'a', type: 'new' }

// イミュータブルな操作(ミュータブルではない操作)
const immutableObj =  { ...obj, type: 'new' };
console.log(immutableObj);
// { name: 'a', type: 'new' }

なぜReactではイミュータブルな操作が必要なのか

上記を踏まえて、Reactでイミュータブルな操作が必要とされる理由は、
React がコンポーネントの変化をオブジェクトの 同一性(差分) チェックで検知しているためです。

ミュータブルな操作をしてしまうとコピー元の情報も変更されてしまうため、
変更前と変更後の差分をReactが検知できなくなってしまいます。

一方、イミュータブルな操作では変更前と変更後の情報をそれぞれ参照しているので、
Reactは差分を検知することができます。

先ほどのオブジェクトの操作を例に取ると、

const obj = {name: 'a', type: 'old'};
// イミュータブルな操作(ミュータブルではない操作)
const immutableObj =  { ...obj, type: 'new' };
console.log(immutableObj);
// { name: 'a', type: 'new' }

obj !== immutableObjとした時にReactはコンポーネントに変化があったと検知することができます。

しかしミュータブル(イミュータブルではない)な操作をした下記の場合、

const obj = {name: 'a', type: 'old'};
// ミュータブルな操作(イミュータブルではない操作)
obj.type = 'new';
console.log(obj);
// { name: 'a', type: 'new' }

obj.type = 'new';
とした後のobjは初めとは異なったtypeの値を持ちますが、
obj !== objでは変更を検知できないため、Reactは差分に気付けないのです。

この変更の検知の仕組みを持つため、Reactではイミュータブルな操作が必要とされるのです。

なのでReactで配列操作やオブジェクト操作をする際には,
concat()Object.assign()スプレッド演算子を用いたイミュータブルな操作をする必要があります。

たどたどしい説明になってしましましたが、最後まで読んで頂きありがとうございました。

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

【ReduxToolkit】React,TypeScript,ReduxToolkitで最新の開発環境を爆速で構築するお話【非同期通信】

はじめに

  • この記事を読むと、React-TypeScript-ReduxToolkitのSliceの作成から非同期通信まで理解することができます。
  • 説明は最小限にとどめ、コードベースで進めていきます。

いざ、React-TypeScript-ReduxToolkitの環境構築

まずは何がともあれコマンドでReact-TypeScript-ReduxToolkitの環境を構築する。

create-react-app my-app --template redux-typescript

そして、以下コマンドで実行

npm run start もしくは yarn start

こんな画面がでれば、開発環境構築完了です。

スクリーンショット 2020-08-29 17.53.28.png

かわいいですよね。Reduxのロゴ。
筆者もこんなん考えれる人になりたいとです…。

Redux-TypeScriptのtemplateの中身を見てみよう

そして、src/feauturesディレクトリを見てみましょう。
Counterディレクトリが入っているはずです。

ReduxToolkitの特徴は

機能ごとにSliceという塊を作って整理していくことです。

なので、新たに機能を追加したい場合はこのtemplateと同じようにfeaturesの中にCounterのようなSliceを作ればOK。

では、非同期通信のSliceを作っていきましょう。

非同期通信のSliceを作成しよう!

お待ちかね、非同期通信のSliceを作っていきましょう。
では、名前をfetchとし、featuresディレクトリの内部に作成してください。
スクリーンショット 2020-08-29 18.03.54.png

その中に2つTypeScriptのファイルを作成しましょう。

  • Fetch.tsx
  • fetchSlice.ts

こんな感じになりましたか?

スクリーンショット 2020-08-29 18.07.40.png

DOMをreturnするファイルは拡張子を.tsxとし、関数などしか定義しないファイルは.tsとすればいい感じになりますね。

では、fetchSlice.tsから書いていきましょう!
今回、非同期通信のAPIはJSONPlaceholderのものを使用させていただきました。

・ JSONPlaceholder
https://jsonplaceholder.typicode.com/users

そして、非同期通信なので、この子も忘れずにインストール

npm install axios もしくは yarn add axios

fetchSlice.ts

import {createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'axios'
import userData from './userData.json'
import {RootState} from '../../app/store'

type UserDataType = typeof userData

type FetchType = {
  data: UserDataType
}

export const fetchGetData = createAsyncThunk('fetch/get', async () => {
  const res = axios.get<UserDataType>('https://jsonplaceholder.typicode.com/users')
  return res
})

const initialState: FetchType = {
  data: userData
}

export const fetchSlice = createSlice({
  name: 'fetch',
  initialState: initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchGetData.fulfilled, (state, action) => {
      return {
        ...state,
        data: action.payload.data
      }
    })
  }
})

export const selectData = (state: RootState) => state.fetch.data

export default fetchSlice.reducer

これで完成です!

そして、app/store.tsをちょっとだけいじりましょう。

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import fetchReducer from '../features/fetch/fetchSlice'
export const store = configureStore({
  reducer: {
    counter: counterReducer,
    fetch: fetchReducer
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

fetchReducerの部分を追加するだけですね!

これにて、Sliceの作成、およびStoreへの格納が完了です。

おつかれさまでした!

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

ReactとChart.jsでグラフを描画する

目的

グラフ描画に定評?がある「Chart.js」。
「react-chartjs-2」を使いReactアプリケーションでの実装方法の手順を理解していきます。
【公式サイト】
Chart.js:https://www.chartjs.org/
react-chartjs-2:https://github.com/jerairrest/react-chartjs-2

完成イメージ

image.png

実装準備

実装に必要なモジュールをインストールしていきます。

Reactアプリケーションを作成

npx create-react-app chart-js

react-chartjs-2をインストール

Chart.js2をReactにラッパーされたものを今回は使っています。
公式サイト:https://github.com/jerairrest/react-chartjs-2

npm install --save react-chartjs-2 chart.js

Chart.jsのコンポーネントを確認

用意されているChart.jsで使用できるグラフのコンポーネントはこちら。
それぞれがどんなグラフかは公式サイトを参照してください。

export class Doughnut extends ChartComponent<ChartComponentProps> {}
export class Pie extends ChartComponent<ChartComponentProps> {}
export class Line extends ChartComponent<LinearComponentProps> {}
export class Scatter extends ChartComponent<ChartComponentProps> {}
export class Bar extends ChartComponent<LinearComponentProps> {}
export class HorizontalBar extends ChartComponent<ChartComponentProps> {}
export class Radar extends ChartComponent<ChartComponentProps> {}
export class Polar extends ChartComponent<ChartComponentProps> {}
export class Bubble extends ChartComponent<ChartComponentProps> {}

実装

今回表示するデータは東京の年間の「降水量の月合計」を表示するグラフを作っていきます。
ちなみにデータは気象庁のページから取得できます。(https://www.data.jma.go.jp/gmd/risk/obsdl/index.php)

では、以下から実装していくっ。

グラフを描画する。

早速実装コードとその画面はこんな感じです。

App.js
import React from 'react';
import { Bar } from 'react-chartjs-2';
import './App.css';

function App() {

  const graphData= {
    labels: [ // 各ラベルを配列にすることで軸ラベルが改行されて表示される
      ['2019年', '1月'],
      ['2019年', '2月'],
      ['2019年', '3月'],
      ['2019年', '4月'],
      ['2019年', '5月'],
      ['2019年', '6月'],
      ['2019年', '7月'],
      ['2019年', '8月'],
      ['2019年', '9月'],
      ['2019年', '10月'],
      ['2019年', '11月'],
      ['2019年', '12月'],
    ],
    datasets: [
      {
        data: [5.6, 7.2, 10.6, 13.6, 20, 21.8, 24.1, 28.4, 25.1, 19.4, 13.1, 8.5],
        label: '月別合計降水量(mm)',
      },
    ],
  };

  return (
    <div className="App">
      {/* グラフコンポーネントの呼び出し */}
      <Bar data={graphData} />
    </div>
  );
}

export default App;

画面
image.png

このようにコンポーネントにdataプロパティを付けることでグラフを表示することが出来ます。
これだけではグラフとしてそっけないので、オプションを追加していきます。

App.js
//(省略)

function App() {

  const graphData = {
    labels: [
      // (省略)
    ],
    datasets: [
      {
        data: [5.6, 7.2, 10.6, 13.6, 20, 21.8, 24.1, 28.4, 25.1, 19.4, 13.1, 8.5],
        backgroundColor: 'rgba(30, 144, 255, 1)', // <--追加
        label: '月別合計降水量(mm)',
      },
    ],
  };

  const graphOption = {
    scales: {
      xAxes: [ // x軸設定
        {
          scaleLabel: { // 軸ラベル設定
            display: true,
            labelString: '2019年',
          },
        },
      ],
      yAxes: [ // y軸設定
        {
          scaleLabel: {
            display: true,
            labelString: '合計降水量(mm)',
          },
          ticks: { // 軸目盛設定
            beginAtZero: true,
            callback: function (value, index, values) {
              return `${value}(mm)`;
            },
          },
        },
      ],
    },
  };

  return (
    <div className="App">
      {/* グラフコンポーネントの呼び出し */}
      <Bar data={graphData} options={graphOption} />
    </div>
  );
}

export default App;

画面はこんな感じ。

image.png

グラフのdatasetsに色を付け、グラフコンポーネントにoptionsを追加しました。。
これだけでも意外とそれっぽくなりますね。
オプションの各項目の詳細は公式サイトを参考にしてください。

まとめ

グラフ描画のライブラリにはD3.jsが人気という話もあるが、簡単なグラフを表示するだけならChart.jsのほうがサクッとでき印象です。
次は複数のグラフを同時に表示する複合グラフを作っていこうと思います。

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

ReduxとFluxはどう違うのか

Reduxはわかった。Fluxとはなんなのだ?

React/Reduxは昨今のWebフロントではよく見る構成なので、学んでいる方は多いと思う。
Reduxの関連文献を読むと度々Fluxという言葉が出てくるがあれは一体なんなのか。

この記事では、Reduxとの違いを意識しながらFluxを紐解いていく。

Fluxとはアーキテクチャである

Facebookが生み出したクライアントサイドウェブアプリ用のアーキテクチャである。
Reduxはライブラリの名前であるが、Fluxはアーキテクチャの名前である。つまり特定のライブラリを指す言葉ではない。
(とはいえfacebook/fluxがutilを提供しており、これが使われることが多い)

このアーキテクチャの実装を助けるライブラリが多数生まれたが、その中で一際人気を誇ったのがRedux。
ReduxはFluxのような単一方向のデータパスに基づく設計思想になっているが構成要素に違いがある。

なぜFluxなのか MVCでは難しい実装ケース

ReduxとFluxの話に入る前に、もう一つ有名なアーキテクチャのMVCの話を最初にする。
MVCモデルではどんな時に不都合なのだろうか。

端的に書くとMVCでは1つのデータソースを複数箇所のUIから操作されるようなケースでコードが複雑になる。
公式の例ではチャットの未読数のカウント表示が挙げられている。

  • チャットは不特定多数の人から送られ、その内どれかが既読になればカウントを-1する
  • 2カ所からチャット開けるならそれぞれの箇所で既読時にカウントを-1するロジックをいれる
  • 2カ所に存在するカウント表示にデータの変更を反映させる

このように、共通のデータを複数箇所から変更 & 参照する場合、Viewのデータソースとなる値をどこでどう管理するのかが難しくなる。
MVCでは複数箇所から更新され、複数箇所に対して更新を反映する複雑なデータフローになるところを、Fluxであれば単一で1方向のデータフローで表現することができる。

Fluxの主要素

下記の要素がFluxの主要素と言われている。

  • dispatcher
  • store
  • view
  • action

いずれもReduxの経験者であればなんとなく耳にしたことがあるような単語ではあるが、それぞれ少しずつ意味合いが異なる。

flux-simple-f8-diagram-with-client-action-1300w.png

図の引用: Flux公式ページ

dispatcher

storeに対してcallbackを登録し、呼び出しに応じてそれを起動する存在。
Action Creatorメソッドから呼び出され、新しいデータを持ったActionオブジェクトをStoreに伝える役割を持つ。
1つのdispatcherが複数のcallbackを登録することができ、データをブロードキャストすることができる。

store

データを管理するオブジェクト。
getterを持つがsetterは持たず、dispatcherによって登録されたcallbackからのみ更新される。
Reduxとは異なり、storeは複数存在することができる。それぞれのstoreに対してdispatcherはcallbackを登録する。

view

storeのデータを参照して作成されるアプリケーションの見た目の部分。
storeからデータを受け取るviewのことを Controller view と呼び、子孫コンポーネントに対してPropsを渡していく役割を持っている。
viewとController viewに分けることで、viewの責務をシンプルに保つことができる。

Action

Actionはストアを更新する際の新しいデータを含んだオブジェクト。
dispatcherに対してActionを渡す関数をAction Creatorと呼ぶのはReduxと同じだが、FluxではActionsというオブジェクトのメソッドとしてAction Creatorが実装されることが多いらしい。(Flux公式でもAction Creatorはmethodであると書かれている)

Fluxコードを体験してみたいなら

facebookがチュートリアル付きのexampleを提供しているので、Readme.mdに従ってコーディングをしてみるとFluxの書き味が体験できる。
flux-todomvc

TodoMVCをFluxで作っていくチュートリアルで序盤は写経できるサンプルと解説、最後には解答無しの練習課題がついているので良い練習になる。
flux/utilを使用するので書き方はReduxに近いものがあり、Reduxでのアプリ制作経験があるの人ならそれほど時間をかけずに終えられると思う。
(筆者はこの記事を書くにあたってトライした)

Reduxとの違い

上記のFluxの特徴と重複する部分はあるが、Reduxの違いにフォーカスしてまとめる。

dispatcherの存在

ReduxにDispatcherは無い。Store.dispatch()というメソッドはあるが別物。
上記の通り、dispatcherはstoreに対してcallbackを登録する役割を持つ。action creatorからDispatcher.dispatch()がcallされることからfluxの中心的な位置にいると言っていい。

Reduxでは、dispatcherが登録するcallbackの役割をreducerが担っている。

action creatorの役割

dispatcherに関連してaction creatorの動きも少しだけ違う。
fluxのaction creatorはactionを引数にしてdispatcher.dispatchをcallする役割がある。

Reduxにおけるaction creatorは呼ばれた時にactionを返却する。
creatorという名前のイメージにはこちらの方が近い。dispatcherとの結合がなくシンプル。

middlewareが存在しない

Fluxはmiddlewareという仕組みを持たない。Dispatcher.dispatch()はあらかじめ登録したCallbackを呼ぶための処理であり、間に他の処理を挟むことはできない。

ReduxはmiddlewareによってdispatchされたActionをロギングしたり、エラーActionのハンドリングを行うなどReduxの処理フロー全体に影響を与えるような処理を含むことができる。
redux-thunk, redux-observableなどの非同期処理用のmiddlewareを活用することで、非同期処理の責務をらAction Creatorに持たせることができるようになるためComponentをシンプルに作りやすい。

Fluxのstateは複数のstoreによって表現される

Fluxは複数のstoreを持つ。例えばTwitterのようなシステムであればUserInfoStoreとTweetListStoreのようなstoreを持つことになる。
これに対してReduxは1つのplain old Javascript object (POJO)でstateを表現する。アプリケーション全体の状態を扱いたいケースにおいては1つのオブジェクトである方が遥かに扱いやすい。
例えばSSRは、サーバーでstateを構築してクライアントに送信する必要があり単一のオブジェクトである恩恵を受けている。

stateを追加する際にはFluxは新たなstoreを作成し、dispatcherから新たなcallbackを登録する。
Reduxは純粋な関数であるReducerを追加するのみであり、拡張がシンプル。

終わりに

ReduxはFluxを基にして生まれたライブラリというだけあって使い勝手が良い面が多い。
Fluxを用いて新規にアプリケーションを作成する機会は少ないと思うが、アーキテクチャの話題では頻出単語なので正しく理解しておくと役立つと思う。

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