- 投稿日:2020-02-08T21:55:47+09:00
react-useのuseMediaを使ったレスポンシブ対応
react-use はhooksにあると便利なものがそろっているライブラリで、スター数的にもそれなりに使われてそうな感じがするので個人で作るときには結構導入してます
const.tsexport 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]こんな感じで書くと良さそう。毎回書くので自分のコピペ用として
- 投稿日:2020-02-08T20:58:03+09:00
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
- 投稿日:2020-02-08T19:09:33+09:00
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()
でState
とActions
を内包したオブジェクトを取得できます。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
を単純に返すもののほか、async
やasync 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 で自作ライブラリ公開したので、変なところ見つけたら指摘いただけたらと思います。
- 投稿日:2020-02-08T14:13:47+09:00
ReactComponentの依存度を解析するツールを作りました?
どんなツールか
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
を使いました。基本的に、ASTExplorerでAST
構造を見て、必要な情報を再帰して取得しています。
また、monorepo
で作ったので、ここの処理はreact-izon-core
というパッケージに実装しています。TypeScript
ツールは、TypeScriptで書きました。TypeScriptにした理由は、VSCodeでのサポートが豊富な事と、
monorepo
で実装が分断されていても、型
によってある程度はやり易くなるためです。oclif
cliツールを作るためのフレームワークです。
TypeScriptに対応していたのと、私がcli
を作るのが初めてだったので使ってみました。
フレームワークなので、cli
に良くある機能が簡単に実装できたので、使って良かったと思いました。
また、eslint
のルールも設定してある物があるので、環境構築にも悩まされないところも良かったです。ドキュメント( 英語 )もあるので、困ったことは特には無かったです。なぜ作ったのか?
チームで
React
を用いて開発していたときに、React
の経験がまだ無い人がチームの中に何人か居て、その人たちに、Propsの流れ
やコンポーネントの複雑性
を説明するのがすごく難しかったことがありました。
そのときに、「フロントエンドが分からない人でも、コンポーネントの複雑さ
やPropsのデータフロー
を伝え易くするツールを作ろう!」と思い作りました。今後の予定( 仮 )
他のフレームワーク・ライブラリに対応する
React
だけでなく、Component
を使う他のフレームワーク・ライブラリに対応したいと思っています。しかし、当分はReact
のみになると思いますが。デザインをもっと良くする
今のデザインはダサいと思っているので、もっと使いやすくて、分かりやすいデザインにして行こうと思っています。
相関図を表示できるようにする
依存関係を図にして、直感的に分かるようにしようと思っています。
この機能をつける事で、チームでの開発に役立ったり、フレームワーク・ライブラリを知らない人にアプリの複雑性などを説明しやすくなると思っています。
Props
など他の要素からも依存関係を調べる今のところ、
Props
の受け渡しなどの情報をうまく活用できてないので、それを使ってデータフローを可視化したいと思っています。テストフレームワークで使えるようにする
jest
やmocha
などで使えるようにしたいと思っていますが、実装するかは謎です。後書き
ここまで読んで下さり、ありがとうございます。
2020/02/05時点では、まだまだ色々な課題を多く含んでおり、目も当てられないような品物ですが、これから改良していって、皆さんの開発に役立つようなツールにして行けたらいいなと思っております。
いつになるかは分かりませんが。それでは?
- 投稿日:2020-02-08T13:53:36+09:00
Reactの高階コンポーネントのメモ
はじめに
React の高階コンポーネントのメモです。
ご指摘、感想、お待ちしています。。。目次
- 高階コンポーネント???
- まずは普通に書いてみる
- 高階コンポーネントを作成
- 高階コンポーネントをくっ付ける
- まとめ
1. 高階コンポーネント???
高階コンポーネントとは。。。
公式の Docs
以下は、引用です。高階コンポーネント (higher-order component; HOC) はコンポーネントのロジックを再利用するための
React における応用テクニックです。HOC それ自体は React の API の一部ではありません。
HOC は、React のコンポジションの性質から生まれる設計パターンです。さらにさらに。
具体的には、高階コンポーネントとは、あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。
ほうほう、どうやらコンポーネントのロジックを再利用(=使い回す)ためのテクニックなのか。。
では、実際に書いてみよう。2. まずは普通に書いてみる
簡単なカウンターのコンポーネントを 2 つ用意します。
カウントの条件は以下
- ボタンのクリック
- マウスでホバー
ClickCounterimport 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);HoverCounterimport 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として持たせてあげます。higherOrderComponentimport 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 higherOrderComponent4. 高階コンポーネントをくっ付ける
高階コンポーネントを利用してみましょう。
higherOrderComponent(ClickCounter)
の箇所で利用がされています。
そうすると、count
やincrementCount
は高階コンポーネントがpropsで渡してくれているので
各コンポーネントで定義やロジックの実装が不要になります。ClickCounterimport 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)HoverCounterimport 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. まとめ
高階コンポーネントを利用して、コンポーネントのロジックを共通化できました。
めでたし、めでたし。
おしまい。
- 投稿日:2020-02-08T11:20:28+09:00
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); }これで解決?
- 投稿日:2020-02-08T07:25:47+09:00
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-importsnext.config.jsconst 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/*"] } } }