20200125のReactに関する記事は5件です。

SPA入門色々(Vue.js/Blazor/React)

概要

SPA(Single Page Application)という技術があることを知り、フロントエンドフレームワークを触ってみるいい機会になるのでないかと思い、Vue.js/Blazor/Reactをまとめて触ってみた。
SPAのルータのページ切り替えを見て従来のiframeや生のJavaScriptとできることがどのように違うのかということも疑問に思ったので同じようなこちらでも同じような感じで実装を試みた。

作ったSPAの仕様

  • 上部にメニューバー
  • Home/Counter/Todoの独立したWebアプリをコンポーネントとして所持。
    • Home
      • 別のコンポーネントを読み込んで文字を表示するだけなアプリ。
    • Counter
      • Blazorの実行サンプルに付いてきたカウンターアプリ。
    • Todo

成果物

Vue.js | Blazor | React | iframe | Vanilla-JS
ソースとか(GitHub)

感じたメリット・デメリット

SPA全体

メリット

  • 本来サーバーサイドで受け持っていたような大幅なhtmlの書き換えが簡単にできる。
  • htmlを分割して部品化できる。
  • 単一htmlとしてシームレスにページ切り替えられる。

デメリット

  • フレームワークありきな存在であること。
  • 読み込み時に時間がかかりがち。
  • サーバーありきな存在なので気軽に公開しにくい。

Vue.js

メリット

  • 本来のhtmlに近い書き方でhtmlを複数ファイルに分割できる(.vue)。
  • 変数をフォームとバインド(同期)できる。
  • v-for\v-ifでタグをループできる。
  • CSSをコンポーネントに閉じ込めることができる。
  • vue-cliがあれこれ揃えてくれるのでセットアップが簡単。
  • 地味にラップされてるWebStorage。

デメリット

  • ルーターの設定が少し煩雑。
  • exportするオブジェクトの構成が少し煩雑。

Blazor

メリット

  • C#だけでほぼ完結して処理を書くことができる。
  • 本来のhtmlに近い書き方でhtmlを複数ファイルに分割できる(.razor)。
  • 変数をフォームとバインド(同期)できる。
  • @for\@ifでタグをループできる。
  • ルーターの設定が容易。
  • イベントハンドラの呼び出しが素直。
  • 他のコンポーネントを呼びだすためのimportが不要。

デメリット

  • まだリリース前。
  • ページを開いた時の読み込みが遅い。
  • @がゲシュタルト崩壊する。
  • DOM由来のAPIを扱うのが面倒。
  • 変数バインドと@onchangeを同時に使うことができない。
  • CSSに対するサポートがない。
  • ライフサイクルが少し心もとない?

React

メリット

  • あくまでもJavaScriptが主体となっている。

デメリット

  • htmlじゃないhtml。
  • htmlとして書くとJSXの形式が直感的じゃない。
  • フォームと変数のバインドに癖がある。
  • stateの更新が手間。
  • 必要なプラグインは基本的物でも各自そろえる必要がある。
  • CSSの扱いに癖がある(スコープ化するのも手間っぽい)。
  • 専用のループ構文は存在しない。

iframe

メリット

  • フレームワークが不要(htmlとJavaScriptの知識だけで良い)。
  • いにしえの枯れた技術。
  • 適当なホームページスペースに上げても動く。

デメリット

  • そもそも、SPAではない。
  • htmlが独立しておりそれぞれの連携が面倒。
  • URLが項目ごとに変わらない。
  • 各フレームのサイズ調整が困難。
  • フォームと変数のバインドとかはない。

生JavaScript

メリット

  • あるかな?

デメリット

  • 生成した要素の状態管理が絶望的。
  • 再レンダリングが困難

まとめ

触り始めたばかりの素人の感覚としてはReactよりもVue.jsが触りやすいもののように思えた。
アニメーションを付けたりする段階まで行くとまた印象が変わってくるのかもしれない。
Blazorはwasmというものの可能性を感じるものだと感じた。
また、全体的な構成がVue.jsと似ており、触りやすいような印象を受けた。
ちなみにAngularは触ろうとしたけど面倒くさくなって投げた(情報量少なくないですか…?)

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

React向けチャートライブラリRechartsで円グラフ

React向けチャートライブラリRechartsで円グラフ

?グラフの種類

AreaChart:面グラフ。折れ線グラフに基づき、定量データを表示したグラフ。
BarChart:棒グラフ。四角い棒の長さで何らかの値を表現するグラフ。
LineChart:折れ線グラフ。散布図の一種であり、プロットされた点を直線でつないだグラフ。
ComposedChart:折れ線グラフ、面グラフ、棒グラフで構成されるグラフ。線のような単一のタイプのチャートを描画する場合は、LineChartを使う。
PieChart:円グラフ。丸い図形を扇形に分割し、何らかの構成比率を表したグラフ。
RadarChart:レーダーチャート。複数の項目の大きさを一見して比較することのできるグラフ。
RadialBarChart:円弧で表現する棒グラフ。
ScatterChart:散布図(分布図)。縦軸、横軸に2項目の量や大きさ等を対応させ、データを点でプロットしたグラフ。
FunnelChart:ファンネルチャート。販売プロセスの段階を表し、各段階の潜在的な収益額を示すためによく使用されるグラフ。
Treemap:ツリーマップ。ネストされた長方形のセットとして階層状のツリー構造のデータを表示したもの。

円グラフに関連するコンポーネント

<PieChart />コンポーネント

円グラフの大元となるコンポーネント。全体のサイズ等を定義します。

☘️プロパティ

  • width : チャートコンテナーの幅。
  • height : チャートコンテナーの高さ。
  • margin : コンテナの周りの空白のサイズ。例){ top: 5, right: 5, bottom: 5, left: 5 }

?イベント

  • onClick
  • onMouseEnter
  • onMouseLeave

?子コンポーネント

  • <PolarAngleAxis />:
  • <PolarRadiusAxis />:
  • <PolarGrid />:
  • <Legend />:
  • <Tooltip />:
  • <Pie />:
  • <Customized />:

<Pie />コンポーネント

グラフのセクター全体についてを定義します。

プロパティ

  • cx : 中心のx座標。パーセンテージを設定した場合、最終値はコンテナ幅のパーセンテージを乗算することにより取得されます。
  • cy : 中心のy座標。パーセンテージを設定すると、コンテナの高さのパーセンテージを乗算することで最終的な値が取得されます。
  • innerRadius : すべてのセクターの内半径。パーセンテージを設定した場合、最終値は、幅、高さ、cx、cyで計算されるmaxRadiusのパーセンテージを乗算することにより取得されます。
  • outerRadius : すべてのセクターの外半径。パーセンテージを設定した場合、最終値は、幅、高さ、cx、cyで計算されるmaxRadiusのパーセンテージを乗算することにより取得されます。
  • startAngle : 最初のセクターの開始角度。円の頂点から反時計回りで始めたい場合は、{startAngle:90, endAngle:450}、同じく円の頂点から時計回りで始めたい場合は、{startAngle:450, endAngle:90}を指定する。
  • endAngle : 最後のセクターの終了角度。
  • minAngle : 各非ゼロデータの最小角度。
  • paddingAngle : 2つのセクター間の角度。
  • nameKey : 各セクターの名前のキー。
  • dataKey : 各セクターの値のキー。
  • legendType : 凡例のアイコンのタイプ。'none'に設定すると、凡例項目はレンダリングされません。'line' 'square' 'rect 'circle' 'cross' 'diamond' 'square' 'star' 'triangle' 'wye' 'none'
  • label : trueの時、値がラベルとして表示されます。デフォルトはfalse
  • labelLine : trueの場合、セクターとラベルを結ぶ線を描画。
  • data : 各要素がオブジェクトであるソースデータ。
  • activeIndex : Pieのアクティブセクターのインデックス。このオプションは、マウスイベントハンドラーで変更できます。
  • activeSphape : アクティブなセクターの形状。
  • isAnimationActive : trueに設定すると、パイのアニメーションが有効化。
  • animationBegin : アニメーションをいつ開始するかを指定します。このオプションの単位はmsです。
  • animetionDuration : アニメーションの継続時間を指定します。このオプションの単位はmsです。
  • animationEasing : イージング関数のタイプ。'ease' 'ease-in' 'ease-out' 'ease-in-out' 'linear' Function

イベント

  • onAnimationStart
  • onAnimationEnd
  • onClick
  • onMouseDown
  • onMouseUp
  • onMouseMove
  • onMouseOver
  • onMouseOut
  • onMouseEnter
  • onMouseLeave

子コンポーネント
- <Cell />
- <LabelList />

<Cell />コンポーネント

各セクターの色や枠を定義します。

プロパティ
- fill : セクターの塗りつぶし色を指定。
- stroke : セクターの枠線の色を指定。
- strokeWidth : セクターの枠線の太さを数値で指定。

<LabelList />コンポーネント

各セクターに対応するラベルを定義します。

<Legend />コンポーネント

グラフの凡例を定義します。

プロパティ

  • width : 凡例の幅。
  • height : 凡例の高さ。
  • verticalAlign : 凡例の位置。
  • formatter : 凡例の項目表示をカスタマイズ。

<ToolTip />コンポーネント

セクターにマウスオーバーしたときのツールチップを定義。
プロパティ
- content : ツールチップをカスタマイズ。

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

react-hooks で redux を書いてみた話

react-hooksでreduxを真剣にいじる機会に恵まれないので、練習がてらボタンをおしたら数字が増えるカウンターを作ってみました

環境構築

まずは環境構築。絶対に必要なredux関連と型情報をワンセットでインストールしましょう。

npx create-react-app hooks --template typescript
npm install react-redux 
npm install @types/react-redux
npm install redux
npm install @types/redux

生成されたテンプレートのプロジェクトをいじっていきます。

action

まずはactionから定義をします。
今回実装するアクションは以下の通りです。

  • カウントが増える INCREMENT
  • カウントが減る DECREMENT
export  const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export function increment() {
    return {
        type: INCREMENT
    }
}

export function decrement() {
    return {
        type: DECREMENT
    }
}

reducer

次に実際に値を更新したり、編集したりするreducerを実装します。

import {INCREMENT, DECREMENT} from "./action";

const initialState = {
    count: 0
}

function reducer(state = initialState, action: {type: string}) {
    switch(action.type) {
        case INCREMENT: 
            return {
                count: state.count + 1
            };
        case DECREMENT: 
            return {
                count: state.count - 1
            };
        default: {
            return {
                count: 0
            }
        }
    }
}

export default reducer;

普通であれば、 combinereducer で結合しますが、今回はreducerを一つしか作らないので、生のreducerをそのままexportしています。

store

値を格納するstoreです。
reducerを受け取ってstoreを生成していますが、このままだとstateの型が不定で後々のつなぎこみで不都合が生じるので、RootStateを定義しています。

import {createStore} from "redux";
import reducer from "./reducer";

export type RootState = ReturnType<typeof reducer>
export const store = createStore(reducer);

ReturnType は関数の戻り値の型として生成して返します。今回であれば、reducerの戻り値(stateのproperty構造)を型として返してくれます。

つまり、今回のRootStateの中身はこのような感じ

type RootState = {
    count:number
}

connect(component)

hooks を使ったものはこの部分が大きく違っています。いままでは、
mpaStateToProps
mapDispatchToProps
connect

を使ってましたが、

useSelector : storeからstateを取得
useDispatch : storeへ変更を伝えるためのdispatch関数を取得

↑の二つを使うだけでよくなります。

先程、作った RootStateuseSelectorに使います。 stateから情報を受け取るのですが、この時stateの情報がないと、型が解決できなくなるので、 RootState を作成して型を確定させるのです。

import React from "react";
import { useSelector, useDispatch} from "react-redux";
import {RootState} from "./store";
import {increment, decrement} from "./action";

export const Counter: React.FC = () => {
    const dispatcher = useDispatch();
    const state = useSelector((state:RootState) => state);

    return (
        <div className="counter">
            <h1> counter </h1>
            <p>{state.count}</p>
            <button onClick={() => dispatcher(increment())} >count up</button>
            <button onClick={() => dispatcher(decrement())} >count down</button>
        </div>        
    )
}

つなぎ込み

あとは、 Providerにstoreを渡して終了です。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {store} from "./store";
import {Provider} from "react-redux"

ReactDOM.render(<Provider store={store}> <App /> </Provider>, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

最後に

mpaStateToProps
mapDispatchToProps
connect

という黒魔術を使わなくてよくなり、且つhooksの文法をしっていれば処理の流れを追いやすくなったと思います。
というわけで、時間があれば昔のreactコードのリファクタを死体お思います。

それでは、よいreactライフを

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

【超初心者】React のcontextAPIを使ってみる

はじめに

reactのcontextAPIについて勉強したのでメモ。

スクリーンショット 2020-01-25 10.39.38.png

最終的には文字を入力して、submitを押すと入力した文字が出てくるようにして、todoアプリのようにしたいのですが、そこにくる前に力着きました。
現状では、文字を入力するとその文字が表示されてます!
初心者なので間違ってたりしたり、アドバイスがあったらコメントよろしくお願いします。

コードは親コンポーネントのApp.jsの中に、inputやsubmitできるbuttonなどがあるform.js と 入力された文字がPrivider Consumerを通して渡されて表示されるtodo.jsの2つが子コンポーネントとしてあります。

参考記事

React Context APIについて

先に上記の記事を読んで、違うバージョンのcontextAPIを見てみたいと思った人用です。上記の記事がめちゃくちゃわかりやすかったです!!しかも動画つき。

実装方法

npm init react-app contextAPI

作った物を yarn start します。

ファイルの構造は

-- App.js
-- src
がありsrcの中にcomponents,contexts
components の中に form.js todo.js
contexts の中に formContext.js

ディレクトリのわかりやすい書き方がわかりませんでした。見にくくてすみません。

コード

App.js
import React from "react";
import "./App.css";
import Form from "./components/form";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: ""
    };
  }

  render() {
    return (
      <div className="App">
        <h1>hello</h1>

      <Form />
      </div>
    );
  }
}

export default App;
src/conponents/form.js
import React from "react";
import Todo from "./todo";
import FormContext from "../contexts/formContext";

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.handleChange = this.handleChange.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
    this.state = {
      text: ""
    };
  }

  onSubmit = event => {
    this.setState({ text: this.state.text });
  };

  handleChange = event => {
    this.setState({ [event.target.name]: event.target.value });
    console.log(event.target.value)
  };
  render() {
    return (
      <div>
        <input
          type="text"
          name="text"
          onChange={this.handleChange}
        />
        <button
          onClick={this.onSubmit}>submit</button>

        <FormContext.Provider value={this.state}>
          <Todo />
        </FormContext.Provider>
      </div>
    );
  }
}
export default Form;

FormContext.Providerでvalueとして上のconstructorのtextをtodo.jsに渡してます。textだけじゃなくて、handleChange、onSubmit などの関数も渡せます。

src/components/todo.js
import React from "react";
import FormContext from "../contexts/formContext";

const Todo = () => (
  <FormContext.Consumer>
    {({ text }) => {
      console.log(text);
      return <div>{text}</div>;
    }}
  </FormContext.Consumer>
);
export default Todo;

ここではtextを受け取ってます。つまり、inputで入力された文字を受け取ってます。

src/contexts/formContexts.js
import { createContext } from "react";

const FormContext = createContext();
export default FormContext;

ここはあまり詳しくわからなかったのですが、Providerとconsumerの間で処理している物らしいです。

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

【OSS貢献記録】emoji-martをReact 17に対応

前回のOSS貢献記録はこちら

はじめに

emoji-martは、下の画像のように絵文字選択フォームを実現するReactコンポーネントです。
スクリーンショット 2020-01-25 9.12.47.png

このコンポーネントを実際に利用していたら、次のような警告文がJavaScriptのコンソールに表示されました。

Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.

* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. 
To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

Please update the following components: NimblePicker

NimblePickerコンポーネントでReact 17で廃止予定のメソッドcomponentWillReceivePropsが使われていて、このままだとemoji-martはReact 17で使用できなくなります。 詳細はReact公式ブログの記事に記載されています。

自分がこの問題に気付いたときはReact初心者でした。なので、とりあえずissueを作って、これを見た誰かに直してもらおうと思いました。しかし、2ヶ月が経っても直される気配がありませんした。

これはまずいと思って、自分で改修できないと決意しました。2ヶ月間Reactを触り続けてReactの理解度が上がっていたので、自分で改修できるのではないかという自信が生まれていました。

改修内容

実際に出したPRはこちらです。

Qiitaの記事『React.js のライフサイクルメソッド componentWillReceiveProps の廃止対応』を参考しながら、改修方法を考えました。Nimble PickercomponentWillReceivePropsではpropsから新しいstateを作っています。なので、今回は記事中の2番目の方法「getDerivedStateFromProps に置き換える」方法をで採用しました。

今気付いたこと

getDerivedStateFromPropsではstateの変更箇所だけを返せばいいので、

static getDerivedStateFromProps(props, state) {
  if (props.skin) {
    return { skin: props.skin }
  } else if (props.defaultSkin && !store.get('skin')) {
    return { skin: props.defaultSkin }
  }
  return null
}

の方が適切でしたね。

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