20200208のReactに関する記事は7件です。

react-useのuseMediaを使ったレスポンシブ対応

react-use はhooksにあると便利なものがそろっているライブラリで、スター数的にもそれなりに使われてそうな感じがするので個人で作るときには結構導入してます

const.ts
export const mediaQuery = Object.freeze({
  xs: '(min-width: 36rem)',
  sm: '(min-width: 48rem)',
  md: '(min-width: 62rem)',
  lg: '(min-width: 75rem)',
})

上記のようにモバイルファーストっぽく書いた場合

const mq = {
  match: [
    useMedia(mediaQuery.lg),
    useMedia(mediaQuery.md),
    useMedia(mediaQuery.xs),
  ],
  colmuns: [4, 3, 2],
}
const colmunIndex = mq.match.findIndex((_) => _)
// どれともマッチしない場合は1に
const colmunLength = colmunIndex === -1 ? 1 : mq.colmuns[colmunIndex]

こんな感じで書くと良さそう。毎回書くので自分のコピペ用として

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

GraphQL Prisma React

目的

この記事は, GraphqQL, Prisma, React を用いて, Webアプリを作った時に, 参考にしたもの(URL)をただ並べているものです. 特にバックエンドの内容(GraphQLとPrisma の組み方など)が多いです.ほとんど自分のメモ用

Prisma

Prisma && MySQL

https://www.prisma.io/docs/prisma-server/database-connector-MYSQL-jgfs/

GraphQL

Apollo

https://www.howtographql.com/react-apollo/0-introduction/

File uploadする時にハマった. 解決策はこれ

https://github.com/ardatan/graphql-import/issues/286

typeDefs

https://oss.prisma.io/content/graphql-import/overview

Prisma && GraphQL

https://blog.logrocket.com/intro-to-graphql-with-prisma/

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

React Context API with Hooks をご機嫌に使うためのライブラリ( tiny-context )を作った

React Context API with Hooks をご機嫌に使うための tiny-context というライブラリを作ったので紹介します。

背景

ちゃかちゃかと State とそれを操作する Action を定義したいけど、 Redux を使うのはちょっと腰が重い・・。
React Context API が Hooks で使えるようになってご機嫌な感じ。だけど Action を合わせて持たせようとすると微妙にハマりポイント多い・・。
React Context API with Hooks に近い感覚で State と Action のセットをお気軽にコンテキストに詰め込めるライブラリを作ってみよう・・・という感じで作りました。

利用イメージ

(少しライブラリアップデートしたのでこの記事もあわせて更新)

管理したい State の型とそれを操作するための Actions を定義します。カウンタアプリな例だと以下の感じです。

type CounterState = { count: number };
const actions = {
  increment: (state: CounterState) => ({ count: state.count + 1 })
};

ライブラリが提供する createTinyContext を呼び出します。実際の Action の挙動をここで定義しますが、先程作成した型を指定することで安全に実装することができます。
Action は 第1引数 および 戻り値 が State となる形で定義していきます。(それ以外の引数は先に定義した Actions と同じ並びで利用できます。)

import { createTinyContext, ExternalActions } from 'tiny-context';

const { Provider, useContext } = createTinyContext<CounterState, ExternalActions<typeof actions>>(actions);

これで Provider と useContext の Hooks が取得できます。これらは通常の Context API や Hooks とほぼ同じ使用感です。

Consumer側としては以下の感じで使います。 useContext()StateActions を内包したオブジェクトを取得できます。

const Buttons = () => {
  const { actions: { increment } } = useContext();
  return <button onClick={increment}>+</button>;
};

const Display = () => {
  const { state: { count } } = useContext();
  return <span>{count}</span>;
};

Provider も React Context API と同じで、以下の感じで利用できます。

const CounterApp = () => (
  <Provider value={{ count: 0 }}>
    <Buttons />
    <Display />
  </Provider>
);

売りなところ

上の利用イメージの通り Context API とほぼ同じ感覚で使えるはず。Context API を使ったことある方であれば理解しやすいかと思います。
Action は State を引数にとり State 返すという形で実装するのでいい感じにテストしやすいはず。
Action は State を単純に返すもののほか、 asyncasync generator で定義することも可能で、以下の通り状態が何度か切り替わるような Action もお手軽に作れます。

class ActionsImpl implements InternalActions<State, Actions> {
  setLock(state: State, lock: boolean) {
    return { ...state, lock };
  }
  async *increment(state: State) { // Async Generator
    state = yield this.setLock(state, true);

    await wait(); // network delays...
    state = yield { ...state, count: state.count + 1 };

    return this.setLock(state, false);
  }
}

利用上の注意点

同一コンテキスト上の Aciton はとにかくシーケンシャルに実行されます。並行で何か処理したいという要件がある場合は Action の中でとじるように処理するか、Action の外で制御する必要があるかと思います。
あと今のところ非同期処理が進んでる途中でのキャンセルはサポートしてないです。

まとめ

この記事を見て 興味湧いた!応援したい! という方いましたら Github でスターいただけると励みになります。
今回初めて npm で自作ライブラリ公開したので、変なところ見つけたら指摘いただけたらと思います。

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

ReactComponentの依存度を解析するツールを作りました?

どんなツールか

image.png

React Componentがどのくらい他のコンポーネントに依存しているかをブラウザで確認できるツールです。

[ 注意 ] : 2020/02/05 時点では、まだベータ版みたいなものなので、バグをとても多く含んでいます。また、制作途中なモノなので実装されている機能がショボいです。。。ご了承ください。

使い方

[ 注意 ] : 現状ではtsxファイルのみに対応しています。js・jsx・tsファイルの動作はまだ確認できていません。

既にnpmで公開しているので、npmからダウンロードして使えます。

$> npm install -g react-izon

$> cd ReactComponentを使っているファイルがある所

$> react-izon ./src/index.tsx <- 一番上のコンポーネントのファイルパスを指定

...

listen to http://localhost:9000

コマンドを実行するとサーバーが起動するので、http://localhost:9000にブラウザでアクセスすると解析結果が表示されます。

グローバルにインストールしたく無い人は、npxでも同じ事が出来るので、npxを使ってください。

$> cd ReactComponentを使っているファイルがある所

$> npx react-izon ./src/index.tsx

...

listen to http://localhost:9000

使った主な技術など

lerna ( monorepo )

monorepoとは、一つのリポジトリで複数のパッケージ(モジュール)を管理することです。
今回作ったツールは、このmonorepoを使ってプロジェクト管理できるlernaと言うツールを使いました。
lernaについては、既に他の解説記事がありますので、そちらを参考にして下さい。

作ったツールは、以下のように機能ごとにパッケージとして切り出してそれぞれが互いを参照し合うような形で開発しました。

  • react-izon : cliの処理を担当するパッケージ
  • react-izon-core : 依存関係などを解析して解析結果を出力するパッケージ
  • react-izon-ui : Reactで描画部分を作り、expressを用いてReactで作ったページをブラウザで見れるようにするパッケージ

このように切り出すことで、役割分担ができて開発がし易くなりました。( 慣れが必要だとは思いますが。。。 )
正直な所、今回のツールではmonorepoでなくても良かった感じがしますが、私自身がmonorepoについて色々と学べたので、良しとしておきます。

@babel/parser( AST )

ASTを用いて、コンポーネントの依存を測っているので、ASTを得るために@babel/parserを使いました。基本的に、ASTExplorerAST構造を見て、必要な情報を再帰して取得しています。
また、monorepoで作ったので、ここの処理はreact-izon-coreというパッケージに実装しています。

TypeScript

ツールは、TypeScriptで書きました。TypeScriptにした理由は、VSCodeでのサポートが豊富な事と、monorepoで実装が分断されていても、によってある程度はやり易くなるためです。

oclif

cliツールを作るためのフレームワークです。
TypeScriptに対応していたのと、私がcliを作るのが初めてだったので使ってみました。
フレームワークなので、cliに良くある機能が簡単に実装できたので、使って良かったと思いました。
また、eslintのルールも設定してある物があるので、環境構築にも悩まされないところも良かったです。ドキュメント( 英語 )もあるので、困ったことは特には無かったです。

なぜ作ったのか?

チームでReactを用いて開発していたときに、Reactの経験がまだ無い人がチームの中に何人か居て、その人たちに、Propsの流れコンポーネントの複雑性を説明するのがすごく難しかったことがありました。
そのときに、「フロントエンドが分からない人でも、コンポーネントの複雑さPropsのデータフローを伝え易くするツールを作ろう!」と思い作りました。

今後の予定( 仮 )

他のフレームワーク・ライブラリに対応する

Reactだけでなく、Componentを使う他のフレームワーク・ライブラリに対応したいと思っています。しかし、当分はReactのみになると思いますが。

デザインをもっと良くする

今のデザインはダサいと思っているので、もっと使いやすくて、分かりやすいデザインにして行こうと思っています。

相関図を表示できるようにする

依存関係を図にして、直感的に分かるようにしようと思っています。
この機能をつける事で、チームでの開発に役立ったり、フレームワーク・ライブラリを知らない人にアプリの複雑性などを説明しやすくなると思っています。

Propsなど他の要素からも依存関係を調べる

今のところ、Propsの受け渡しなどの情報をうまく活用できてないので、それを使ってデータフローを可視化したいと思っています。

テストフレームワークで使えるようにする

jestmochaなどで使えるようにしたいと思っていますが、実装するかは謎です。

後書き

ここまで読んで下さり、ありがとうございます。
2020/02/05時点では、まだまだ色々な課題を多く含んでおり、目も当てられないような品物ですが、これから改良していって、皆さんの開発に役立つようなツールにして行けたらいいなと思っております。
いつになるかは分かりませんが。

それでは?

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

Reactの高階コンポーネントのメモ

はじめに

React の高階コンポーネントのメモです。
ご指摘、感想、お待ちしています。。。

目次

  1. 高階コンポーネント???
  2. まずは普通に書いてみる
  3. 高階コンポーネントを作成
  4. 高階コンポーネントをくっ付ける
  5. まとめ

1. 高階コンポーネント???

高階コンポーネントとは。。。
公式の Docs
以下は、引用です。

高階コンポーネント (higher-order component; HOC) はコンポーネントのロジックを再利用するための
React における応用テクニックです。HOC それ自体は React の API の一部ではありません。
HOC は、React のコンポジションの性質から生まれる設計パターンです。

さらにさらに。

具体的には、高階コンポーネントとは、あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。

ほうほう、どうやらコンポーネントのロジックを再利用(=使い回す)ためのテクニックなのか。。
では、実際に書いてみよう。

2. まずは普通に書いてみる

簡単なカウンターのコンポーネントを 2 つ用意します。
カウントの条件は以下

  • ボタンのクリック
  • マウスでホバー
ClickCounter
import React, { Component } from "react";

class ClickCounter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  incrementCount = () => {
    this.setState(prevState => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.incrementCount}>
          clicked {this.state.count} times
        </button>
      </div>
    );
  }
}

export default UpdatedComponent(ClickCounter);
HoverCounter
import React, { Component } from "react";

class HoverCounter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  incrementCount = () => {
    this.setState(prevState => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    return (
      <div>
        <h3 onMouseOver={this.incrementCount}>
          Hover {this.state.count} times
        </h3>
      </div>
    );
  }
}

export default HoverCounter;

ここで、各コンポーネントで共通の箇所は以下の部分です。

constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }

incrementCount = () => {
    this.setState(prevState => {
        return { count: prevState.count + 1 }
    })
}

3. 高階コンポーネントを作成

冒頭の引用にあるように

あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。

高階コンポーネントを作成しましょう。

  • 高階コンポーネント : higherOrderComponent
  • 受け取るコンポーネント : OriginalComponent
  • 新規のコンポーネント : NewComponent

NewComponentで共通箇所として注目した

  • count
  • インクリメントに必要なincrementCount

を定義しています。
それらを、OriginalComponentのpropsとして持たせてあげます。

higherOrderComponent
import React from 'react'

const higherOrderComponent = OriginalComponent => {
    class NewComponent extends React.Component {

        constructor(props) {
            super(props)

            this.state = {
                count: 0
            }
        }

        incrementCount = () => {
            this.setState(prevState => {
                return { count: prevState.count + 1 }
            })
        }

        render() {
            return <OriginalComponent count={this.state.count} incrementCount={this.incrementCount}/>
        }
    }
    return NewComponent
}
export default higherOrderComponent

4. 高階コンポーネントをくっ付ける

高階コンポーネントを利用してみましょう。
higherOrderComponent(ClickCounter)の箇所で利用がされています。
そうすると、countincrementCountは高階コンポーネントがpropsで渡してくれているので
各コンポーネントで定義やロジックの実装が不要になります。

ClickCounter
import React, { Component } from 'react'
import higherOrderComponent from './Counter'

class ClickCounter extends Component {

    render() {
        return (
            <div>
                <button onClick={this.props.incrementCount}>clicked {this.props.count} times</button>
            </div>
        )
    }
}

export default higherOrderComponent(ClickCounter)
HoverCounter
import React, { Component } from 'react'
import higherOrderComponent from './Counter'

class HoverCounter extends Component {

    render() {
        return (
            <div>
                <h3 onMouseOver={this.props.incrementCount}>Hover {this.props.count} times</h3>
            </div>
        )
    }
}

export default higherOrderComponent(HoverCounter)

5. まとめ

高階コンポーネントを利用して、コンポーネントのロジックを共通化できました。
めでたし、めでたし。
おしまい。

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

React Class内での関数定義(bindしよう)

はじめに

個人的なメモレベルです。

メソッド記法での問題点

ReactでsetStateを利用する際に、JSXで、以下のように書くことが多いかなと思う。

クラスのプロパティとして関数を定義
doPlus = () => {
    this.setState({count: this.state.count + 1});
};

<button onClick={this.doPlus}>ボタンを押したらプラスするよ</button>

しかし、以下のような書き方の場合、初期化時にbindしとかないとエラーになります。

これクラス内に定義されたメソッドであっても、実行方法によって、thisの参照先は変わってしまうJSの仕組み故。この場合、buttonに関数オブジェクトをそのまま渡しているので、thisの参照先buttonになってしまう。

JSの初心者が最初にぶつかる壁はthisの扱いですよね。。。

ボタンを押下すると、undefindeエラーとなる
doPlus(){
    this.setState({count: this.state.count + 1})
}

<button onClick={this.doPlus}>ボタンを押したらプラスするよ</button>

上記のようにメソッド記法で書きたいんだ!!!て場合は、classの初期化時にthisを必ずbindしておきましょう。

初期化処理
constructor(props) {
    super(props);
    this.state = {
        count: 0
    }
    this.doPlus = this.doPlus.bind(this);
}

これで解決?

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

Next.js+TypeScriptでパスのaliasを設定する

Next.js + TypeScript のプロジェクトで相対パス辛いなとなったので、そのエイリアスの設定方法
import { SomeComponent } from '@/components/SomeComponent みたいなやつです

Next.js での path alias

with absolute imports の example があります
https://github.com/zeit/next.js/tree/master/examples/with-absolute-imports

next.config.js
const path = require('path')

module.exports = {
  webpack(config, options) {
    config.resolve.alias['@'] = path.join(__dirname, 'src')
    return config
  },
}

TypeScript での path alias

https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む