20190417のReactに関する記事は6件です。

GitHub Awesomeレポジトリ内の各レポジトリのスター数を見える化する

GitHub AwesomeレポジトリのREADMEに含まれる各GitHubレポジトリのURLにスター数を表示する(一覧化する)ための必要最低限のWebアプリです。(注意:GitHub Tokenを利用しないと、GitHub APIの制約で約50~60個のGitHubレポジトリにしかスター数が表示されません)

https://playground-d4915.firebaseapp.com/

皆さんのGitHub Awesomeレポジトリ巡りが少しでも快適になれば幸いです :dizzy::dizzy::dizzy:

topN.png
starInfo.png

背景

GitHubにはプログラミング言語のお勧めまたは人気のライブラリ、フレームワーク、URL等をまとめてくれているAwesomeレポジトリが数多く存在します。Awesomeレポジトリを活用することでフレームワークやライブラリの候補を洗い出す作業等を大幅に短縮できます。

しかしAwesomeレポジトリは各分野のライブラリやフレームワークを対象としてまとめているため、Awesomeレポジトリによっては項目数が数百~数千に及ぶことがあります。このため一つ一つのページを開いてStar数を確認して優先度付けするような作業もかなり大変です。このような悩みを解決するために各GithubレポジトリのURLにスター数が表示するための最低限のWebアプリを実装して、どこでも使えるようにFirebase上に配置しています。

NOTE:
一番最初にReactを試すときに各種ReactコンポーネントとTypeScriptを試すために作ったものなので、コードもデザインはほぼ何も考えていません。特にスマホからだと非常に使いづらいですが、一応使うことはできます。

WebアプリのURL

以下のURLからアクセスできます。

https://playground-d4915.firebaseapp.com/

使い方

画面上部にある入力欄に必要な情報を入力してGETボタンをクリックします。

  1. URL欄にAwesomeのGithubレポジトリURLを入力する
  2. Token欄にGithubアカウントのGithubトークンを入力する
    • 未入力の場合はGithubトークン無しで実行する
      • トークン無しの場合、Github APIの制約によりGithubレポジトリ50個弱分のGithubレポジトリのスター数しか取得できない。詳細は注意事項の節を参照。
    • 使い捨てのGithubトークンを使用することを推奨
  3. Top List Number欄に表示するTop Nの数字を入力する
    • 結果が出た後に入力してTop Nの値を変更することも可能

クリックするとAwesomeページに含まれる各Github URL情報をGithub API経由で取得します。全ての取得が完了すると、結果として以下のものを表示します。(テーブルと棒グラフはReactコンポーネントの使い勝手を試すだけに入れてます)

  • Top Nのテーブル
  • Top Nの棒グラフ
  • スター文字およびスター数を埋め込んだAwesomeページ
    • リンク先がGithubレポジトリURLでない場合、スター数は表示されない
    • Github APIの実行回数超過エラー等が発生した場合は、スター数に-1が表示される。

注意事項 & Githubトークンの設定方法

基本的にクライアントのブラウザ側で「各Githubレポジトリ情報をGithub API経由で取得する」ように実装しています。Githubトークン無しの場合は1時間あたり50回しかAPI呼び出しを行えないため、GithubレポジトリのURLが多いAwesomeレポジトリだと50件弱までしかスター数を取得できず使い物になりません。

NOTE:
Github APIの呼び出し上限を超過すると、それ以降はRate LimitによりGithub APIの応答結果が403 Forbiddenとなります。

このためGithubレポジトリのURLが多いAwesomeレポジトリの場合は、Githubトークンを入力することがほぼ必須となります。Githubトークンを使用する場合は1時間あたり5000回API呼び出しを行えます。

GitHub「Personal access tokens」の設定方法

Githubトークンを入力するのに抵抗がある方もいると思います。このためGithubトークンは使い捨てのものを作成して使用することをお勧めします。

ソースコード置き場

ソースコードは下記URLに配置しています。改造、流用、再配布などは自由にしていただいて構いません。Githubレポジトリは一時的な作業用にしか使っていませんが、極力削除しないようにします。

https://github.com/yoichiwo7/playground/tree/master/awesome-star/awesome-star-react

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

入門 React:簡単なまとめ

dapps開発を行なっており、フロントエンドはReactで開発することになったのでReactを勉強します。
ちなみにプログラミングに関してはRailsを少しできるくらいで全くの素人です。
勉強した内容に関して簡単にまとめたいと思います。

Reactとは

2013年、Facebookが開発した、Webアプリの User Interface を構築するための Java Script ライブラリです。
仮想DOMなどを利用して余計な機能を排除し、View部分に特化しています。

ソースコード

https://github.com/backstopmedia/bleeding-edge-sample-app

JSX

JSXとは(Java Script XML)

ReactのコンポーネントないでHTMLのような可読性の高く記述できる構文です
メリット:よく知られている、意味的にわかりやすい、構造の可視化

コンポーネント

Reactの中心概念です。
基本的な書き方

sample.js
class diver extends Component {
  render() {
    return (
      <div>
        <p>Hello world</p>
      </div>
    );
  }
}

JSXとHTMLの違い

JSXとHTMLは基本的に同じですが若干異なるところがあります。

・HTMLのclassは、JSXではclassName

・属性値を{}で括ることにより動的な属性値を記述できます

var classId = this.props.id
...
<div class = {classId}>...</div>

・条件分岐はif elseなどではできない。

・三項演算子
・変数に値を代入し参照する
・条件文を別の関数として呼び出し
・&&
といったものを利用します

・三項演算子を使った例

sample.js
{ this.state.loading
  ? <div id="loader"><p>Loading...</p></div>
  : <p>Done!</p>
}

key ref dangerouslySetInnerHTMLといった属性がある

render() {
  return (
    <div>
      <input ref="textInput" ... /></input>
    </div>
  );
}

このrefの設定により、{this.refs.textInput}で参照できるようになっています。

・スタイルはキャメルケースで参照

<div style={styles}>
  <p>Hello world</p>
</div>

var styles= {
  width: 350px,
  margin-bottom: 10px,
}

コンポーネントのライフサイクル

コンポーネントは作成時、作成後、破棄時の3つのイベントで処理を登録する手段があります。

React component ライフサイクル図

データフロー

Reactでのデータの流れは基本的に親から子への一方通行です
そして、propsstateの2つで、データの流れは扱えます。

props

propTypes

データをバインディングできます

getDefaultProps

state

コンポーネントの内部状態を示すものです。

stateの更新

replaceStatesetStateの2種類ありますが、setStateが使いやすいのでsetStateを主に使います。

this.setState({ update: ---- })

イベントの処理

ReactのイベントはJavaScriptのイベントと基本的に同じです
例えば、
・MouseEvent:ユーザーがクリックした時の通知
・change:要素の内容が変わった場合の通知

SystematicEvent

sample.js
class diver extends Component {
  handleCreate (event) => {
    this.setState({ update: event.target.value })
  }
  render() {
    return (
      <div>
        <input onChange={this.handleCrate} />
      </div>
    );
  }
}

コンポーネントの合成

Mixin

複数のコンポーネント間で共有できるメソッドを定義できます。

DOM操作

どうしてもDOMにアクセスしたい場合は、ref属性を設定することで特定のコンポーネントにアクセスできます。

フォーム

Uncontrolled Component

複雑なフォームの作成に向いており、直接DOMノードへアクセスします。
そのため特殊なパターンであると言えます。

smaple.js
class UncontrolledComponent extend component {
  render () {
    return (
      <input 
        type="text"
        defaultValue="Hello world" />;
    )
  }
}

このように、UncontrolledComponentのコンポーネントとは関係なく、Inputは独自の値を保有しています。

smaple.js
class UncontrolledComponent extend component {
  submitHandler: function (event) {
    event.preventDefault();
    var hello = React.findDOMNode(this.refs.hello).value;
  }
  render () {
    return (
      <form onSubmit={this.submitHandler}>
        <input 
          type="text"
          defaultValue="Hello world"
          ref="hello" />;
    )
  }
}

Inputref属性を記述することによって、this.refsでアクセス可能になっています。

Controlled component

下記のInputstateにより保持されているのでControlled componentと言えます。

smaple.js
class ControlledComponent extend component {
  handleChange = e => {
    this.setState({ hello: e.target.value})
  }
  submitHandler = e => {
    event.preventDefault();
    ....
  }
  render () {
    return (
      <form onSubmit={this.submitHandler}>
        <input 
          type="text"
          defaultValue="Hello world"
          value={this.state.hello}
          onChange={this.handleChange} />;
      </form>
    )
  }
}

ラベル

フォームが何を意味しているかを、ユーザーに伝えるため利用します。

Sampele.js
<label htmlFor="name">名前</label>

select

sample.js
<select value={this.state.hoge} onChange={this.handleChange}>
  <option value="A">選択肢A</option>
  <option value="B">選択肢B</option>
  <option value="C">選択肢C</option>
</select>

Checkbox

smaple.js
class SampleCheckBox extend component {
  constructor(props) {
    super(props)
    this.state = {
      checked: true,
    }  
handleChange = e => {
    this.setState({ checked: e.target.checked})
  }
  submitHandler = e => {
    event.preventDefault();
    console.log("checked:", this.state.checked)
    ....
  }
  render () {
    return (
      <form onSubmit={this.submitHandler}>
        <input 
          type="checkbox"
          checked={this.state.checked}
          onChange={this.handleChange} />;
      </form>
    )
  }
}

フォーカス

Autodocusを使うと、ユーザーがブラウザを開いた時に、
クリックする必要なくformを入力できるようになります。

input.js
<input type="text" autoFocus={true} />

ユーザビリティー

ユーザビリティーの向上には下記の5つを考えると良いです。

ユーザーのやることが明確である

placeholderlabel等を使って何をやれば良いのか明確にする

ユーザーの入力に即座に応答する

処理に時間がかかる場合、「ロード中」と出すだけでもユーザーの忍耐力は変化する

予測可能であること

アクセシビリティー

マウスだけ、タッチだけ、といった一定の制約下でテストを行ってみる

入力項目をなるべく少なくする

オートコンプリートなどを利用して、「たくさん入力する必要がある」とユーザーに感じさせない

Java Script

アロー関数、スプレッド構文、に関してはReactを勉強する際に覚えておくよいJavaScriptの概念になります。

アロー関数

functionを省略して書くことができる

// 一般的な関数
var func = function(str) {
  console.log(str);
}

// アロー関数
var func = (str) => {
  console.log(str);
}

スプレッド構文

Sample.js
// 従来の書き方
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);

// スプレッド構文
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);

参考サイト

JavascriptがわかってないとReactもわからないことが多くなるため
ES2015(ES6) 入門

30分間React入門「いいねボタン」作成チュートリアル

Tutorial: Intro to React

今から始めるReact入門 〜 React の基本

イベントリスナー
イベントパンドラ
DOMノード

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

React-Day-Pickerのスタイルを変える方法

日付選択機能が拡張しやすそうというで理由でblueprintというUI Toolkitを使っているのだが、実際に使ってみて見た目を変えたいなと思った所、結構癖があったのでメモしていく。

blueprintのDatePickerはReact-Day-Pickerのラッパー的なもの

blueprintのDatePickerはReact-Day-Pickerをベースにしていて、プロパティ経由で簡単にReact-Day-Pickerのプロパティを指定することができる。

ので、見た目を変えるときはReact-Day-Pickerの機能を使う必要が出てくる。

React-Day-Picker Styling

export function Test() {
  const customProps = { ... } //React-Day-Pickerの設定
  return <DatePicker dayPickerProps={customProps} />
}

スタイルを変えるときに取ることが出来る方法は以下のものがある。

ここで注意するのが、日にちに対するスタイルの指定とUI全体のスタイルの指定の二種類ある
(classNameも用意されている)

カレンダーの日にちのスタイルを変更する

  • modifiers
  • modifiersStyles

DayPicker全体のスタイルを変更する

  • classNames

日にちのスタイルを変更する

React-Day-Pickerではある日にちをいじりたいときはmodifiersプロパティを使って指定する形をとっている。

その際グループ化したい日にちのパターンとその名前という形で設定していく。

設定したグループの日にちにはDayPicker-Day--<グループ名>という名前のCSSクラスの名前が与えられるので
それに対してスタイルを設定すると好きな見た目に変更することが出来る。

もちろん自動生成されたCSSクラス名をCSSファイルに書かないといけない訳ではなく、modifiersStylesにグループ名をキーをとしたinline-Styleを設定すればいい。

Matching days with modifiersにあるサンプルコードをちょっと変えたもの。

import React from 'react';
import DayPicker from 'react-day-picker';
import 'react-day-picker/lib/style.css';

const birthdayStyle = `.DayPicker-Day--highlighted {
  background-color: orange;
  color: white;
}`;

const modifiers = {
  highlighted: new Date(2018, 8, 19),
};
const modifiersStyles = {
  highlighted: {
    color: '#FF0000'
  }
}
export default function MyBirthday() {
  return (
    <div>
      <style>{birthdayStyle}</style>
      <DayPicker
        modifiers={modifiers}
        modifiersStyles={modifiersStyles}
        month={new Date(2018, 8)} />
    </div>
  );
}

日にちの指定の仕方

グループを作る際の日にちの指定には以下の種類があるので適当に使い分ける。

グループは複数指定でき、ある日にちが二つ以上のグループにあるときはその全てが適応される。

  • from/toキー この二つに与えたDateオブジェクトの範囲内の日にちをグループ化する
  • beforeキー 与えた日にちより前のものをグループ化する
  • afterキー 与えた日にちより後のものをグループ化する
  • daysOfWeek 指定した曜日でグループ化する。曜日は0から6の範囲を取り、それぞれ日曜から土曜に対応している。配列で渡すと複数の曜日を指定できる。
  • function(day: Date):boolean グループ化する日にちのときはtrueを返すDateオブジェクトを受け取る関数
const from_to = [
  from: new Date(2018, 8, 19),
  to: new Date(2018, 9, 19),
]
const before_after = {
  after: new Date(2018, 9, 1),
  before: new Date(2018, 10, 1)
}
const daysOfWeek = {
  daysOfWeek: [0] // 日曜日
}
const func = (day) => { return day.getDate() % 2 === 0}

export default function Grouping() {
  return (
    <div>
      <style>{birthdayStyle}</style>
      <DayPicker
        modifiers={[from_to, before_after, daysOfWeek]} />
    </div>
  );
}

用意されているグループ

ちなみにtodayoutsideが予め用意されており、それぞれ今日の日にちと現在の月以外の日にちが対象になる。

イベントとの組み合わせ

グループはDayPickerのイベントが起きたときに日にちを識別する目的にも使うことが出来る。

グループ情報を参照することが出来るイベントにはmodifiersがオブジェクトとして引数に渡されていて、
そのオブジェクトには所属しているグループの名前をboolean型として持っている。

//ドキュメントから拝借
import 'react-day-picker/lib/style.css';

export default class EventHandlers extends React.Component {
  constructor(props) {
    super(props);
    this.handleDayClick = this.handleDayClick.bind(this);
    this.handleDayMouseEnter = this.handleDayMouseEnter.bind(this);
  }

  handleDayMouseEnter(day, { firstOfMonth }) {
    if (firstOfMonth) {
      // Do something when the first day of month has been mouse-entered
    }
  }

  handleDayClick(day, { sunday, disabled }) {
    if (sunday) {
      window.alert('Sunday has been clicked');
    }
    if (disabled) {
      window.alert('This day is disabled');
    }
  }

  render() {
    return (
      <DayPicker
        disabledDays={new Date()}
        modifiers={{
          sunday: day => day.getDay() === 0,
          firstOfMonth: day => day.getDate() === 1,
        }}
        onDayClick={this.handleDayClick}
        onDayMouseEnter={this.handleDayMouseEnter}
      />
    );
  }
}

と、かなり汎用性があるものになっている。

DayPicker全体のスタイルを変更するときはclassNames用のテンプレートを使おう

日にちについてはかなり柔軟なカスタマイズが出来るようになっているが、
それ以外の例えばヘッダー部分とかの背景色を変更したいときはclassNamesプロパティを使う。

classNamesにはDayPickerで使われる要素全てにCSSを指定する形になっている。

各要素には使用するCSSクラスを指定するのだが、要素の数は結構あるので公式から用意されているテンプレートを書き換えるのがいいと思う。

テンプレートはこちら react-day-picker/src/classNames.js

テンプレートのインターフェイスはimport {ClassNames} from 'react-day-picker'からインポート出来る。

function makeClassNames(config) {
  const DEFAULT = {
    container: 'DayPicker',
    wrapper: 'DayPicker-wrapper',
    interactionDisabled: 'DayPicker--interactionDisabled',
    months: 'DayPicker-Months',
    month: 'DayPicker-Month',

    navBar: 'DayPicker-NavBar',
    navButtonPrev: 'DayPicker-NavButton DayPicker-NavButton--prev',
    navButtonNext: 'DayPicker-NavButton DayPicker-NavButton--next',
    navButtonInteractionDisabled: 'DayPicker-NavButton--interactionDisabled',

    caption: 'DayPicker-Caption',
    weekdays: 'DayPicker-Weekdays',
    weekdaysRow: 'DayPicker-WeekdaysRow',
    weekday: 'DayPicker-Weekday',
    body: 'DayPicker-Body',
    week: 'DayPicker-Week',
    weekNumber: 'DayPicker-WeekNumber',
    day: 'DayPicker-Day',
    footer: 'DayPicker-Footer',
    todayButton: 'DayPicker-TodayButton',

    // default modifiers
    today: 'today',
    selected: 'selected',
    disabled: 'disabled',
    outside: 'outside',
  };
  return Object.assign({}, DEFAULT, config)
}

export default function CustomClassNames() {
  //
  const classNames = makeClassNames({
    container: 'changeBackground'
  })
  return (
    <DayPicker 
      classNames={ styles } 
      modifiers={{
        [styles.birthday]: new Date(2018, 8, 19)
      }}
    />
  );
}

ちなみにデフォルトで設定されているクラスを完全に上書きしてしまうとレイアウトが崩れたので、次のように追記する形にしたほうがいいと思う。

指定したクラス名はそのまま設定されるようなので、classnamesパッケージとか利用したら便利。

const custom = {
  caption: classnames('DayPicker-Caption', 'appendStyles')
}

もちろんCSSModuleも使える

ので、好きな方法でスタイルをカスタマイズすることが出来る。

かなり拡張性が高いパッケージになっていて、日時選択にはこれが一番融通がきくのではないかなと。

おすすめのパッケージです。

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

React.useEffectで非同期処理をする場合の注意点2つ

はじめに

React 16.8から導入されたhooksにはuseEffectがあります。

詳細は公式サイトをまず参照しましょう。

useEffectを使うと、コンポーネントのレンダリングとは別に処理を書くことができます。useEffectでしばしば非同期処理を書くことがあります。例えば、サーバからのデータ取得の処理などがあります。

以下では、useEffectで非同期処理を書く場合の注意点を2つ紹介します。ケースによっては注意点はこの2つだけではない可能性が高いので、ご留意ください。

promiseを返さない

useEffectに渡す関数の戻り値はcleanup関数です。

useEffect(() => {
  console.log('side effect!');
  const cleanup = () => {
    console.log('cleanup!');
  };
  return cleanup;
}, []);

cleanup関数は次のeffectが呼ばれる前やアンマウントする場合に呼ばれます。(depsが[]なのでこの例では後者のみ)

よって、下記は間違いです。

useEffect(async () => {
  await new Promise(r => setTimeout(r, 1000));
  console.log('side effect!');
}, []);

このコードはcleanup関数の代わりにpromiseを返してしまっています。
正しくは、下記のようにします。

const sleep = ms => new Promise(r => setTimeout(r, ms));

useEffect(() => {
  const f = async () => {
    await new Promise(r => setTimeout(r, 1000));
    console.log('side effect!');
  };
  f();
}, []);

アンマウントのフラグを持つ

非同期処理を書く場合、コンポーネントが削除された後にコールバックが呼ばれる場合があります。この時、コンポーネントのステートを変更しようとするとワーニングがでます。

const [count, setCount] = useState(0);
useEffect(() => {
  const f = async () => {
    await new Promise(r => setTimeout(r, 1000));
    setCount(c => c + 1);
  };
  f();
}, []);

これを回避するには次のようにアンマウントのフラグを持ちます。

const [count, setCount] = useState(0);
useEffect(() => {
  let unmounted = false;
  const f = async () => {
    await new Promise(r => setTimeout(r, 1000));
    if (!unmounted) {
      setCount(c => c + 1);
    }
  };
  f();
  const cleanup = () => {
    unmounted = true;
  };
  return cleanup;
}, []);

おわりに

React HooksのuseEffectについて非同期処理を使う場合のよくあるケースの注意点について紹介しました。React Hooksはまだベストプラクティスが溜まっていないため、今後違う方法が主流になる可能性はある点についてはご注意ください。

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

SSRの時間計測方法(Nextjs)

Nextjsはlibの中にrenderToStringが入っていて純粋にrenderToStringのみの計測ができなそうなので、以下のような感じで妥協するしかなさそう。

const serverTiming = require('server-timing')

const ssrTiming = async ({ req, res, pagePath, queryParams }) => {
  res.startTime('ssr', 'SSR')
  const html = await app.renderToHTML(req, res, pagePath, queryParams)
  res.endTime('ssr')
  res.send(html)
}

app.prepare()
  .then(() => {
    const server = express()
    server.use(serverTiming())

    // SSRの時間を計測したいパス
    server.get('/', (req, res) => {
      return ssrTiming({ req, res, pagePath: '/' }))
    }

    server.get('*', (req, res) => {
      return handle(req, res)
    })

    server.listen(port, (err) => {
      if (err) throw err
      console.log('> Ready on http://localhost:' + port)
    })
  })
  .catch((ex) => {
    console.error(ex.stack)
    process.exit(1)
  })
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでpropsを渡すときに使う「...」ドット3つ

render() {
 const obj = {
            a:1,
            b:2
        }

 return (
 <Component {...obj} />
 )
}

なんか気持ち悪いなと思ったらjsx(React.jsでhtmlをつくること)特有の書き方らしい。
以下のようにして書くのと同じこと。

render() {
 const obj = {
            a:1,
            b:2
        }

 return (
 <Component a={1} b={2} />
 )
}

ひとつひとつpropsを定義してあげて渡すか、
オブジェクトを展開してあげてまとめて渡すかの違い。

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