20190522のReactに関する記事は4件です。

バックエンドエンジニアがReact(JS)+Gin(Golang)でwebサービスを作ってみた

webサービスを作ってみたのでその紹介と、作ってみた感想です。特定の技術に関する説明をする記事ではありません。

どんなサービスか

色の名前とそれに紐付くRGB値等の定義はHTMLJIS慣用色名などがありますが、仮に「赤」という色が定義上#ff0000だとしても、多くの人は#ff0101を見たときも「赤」だと認識すると思います。本来の定義からどれくらい外れていたらその色と認識しなくなるかは、人によって違いそうです。

これについて自分以外の人がどのように認識しているか知りたいと思いました。その方法としては、「赤」という色の名前とともに#ff0000に近い色を複数表示して、「赤」だと思うものを選択する、という作業を複数人にしてもらうことで、表示されたそれぞれの色についてどの割合でそれを「赤」だと思う人がいるか知ることができそうです。

この情報を収集してうまく表現できれば「これは赤では?」「うーん、違う気がする」といったシチュエーションに対して「ではどの割合でそう思う人がいるか調べてみよう?」という解決策をもらたすことができますよね?

これを実現することを目標にしたのが今回紹介するcolor-consensusというサービスになります。

以下が使い方の説明です。

  • まずアクセスすると以下のような画面が出てきます。右側のナビゲーションでGreenを選んでますから、画面中央のマス目からGreenだと思う色を選んでほしいという画面です。クリックやドラッグで選択できます。

  • 入力した情報をSubmitするにはログインが必要です。非ログイン状態でSubmitを押したり右上のそれっぽいボタンを押すとサインアップかログインができるモーダルが開きます。ユーザがログインIDとパスワードを指定する方式ではないです。ユーザ情報を入力してSubmitするとサービスから一方的にログインIDを発行してSet-Cookieします。そのログインIDは今まで右上に表示されていたサインアップ/ログインボタンの部分に表示され、クリックでクリップボードにコピーできます。

  • 色を選んだりログインしたりしたくない場合は灰色のGo to statistics →ボタンから統計情報を表示する画面に遷移できます。

  • 色を選んでSubmit →を押しても同様に統計情報を表示する画面に遷移します。その画面では中央上のフィルターに沿って選択された色が白い線で囲われます。何人の人がSubmit →したかもVote Countとして表示されています。

  • フィルターをいじることで人の属性を限定して結果を表示できます。現状日本人の僕しかSubmitしていないのでJapanしかないですね。

作ってみたサービスはこちらです。 → https://color-consensus.herokuapp.com/

ソースコードも公開しています。 → https://github.com/konohiroaki/color-consensus

コードの規模はテストを含めて7000行くらい。フロントエンドとバックエンドの割合はバックエンドの方が半分より少し多いくらいです。

開発期間は3~4ヶ月ほどで、なるべく毎日触るようにしながら、焦らず少しずつ進めていました。

技術スタック

仕事では Java8, Spring Framework, Cloud Foundary あたりを使ったwebのバックエンド開発をメインでしています。今回の開発を通してjQueryあたりで止まっているフロントの知識をアップデートしたいと思いました。

フロントエンド

Webpack, Babel, Javascript, React, Redux, bootstrap, など
- Webpack以外の選択肢もあるようでしたが、Webpackという名前をよく聞くのでそれを選ぶことにしました。
- VueとReactは悩みましたがReactのドキュメントを読み始めたらするする読めたのでそのままそれを使うことにしました。

バックエンド

Golang, Gin framework, MongoDB, など
- 仕事で使っている技術そのままでもできましたが、バックエンド開発でも新しいことを学ぶために最近流行りのGolangを使うことにしてみました。
- webフレームワークはGolang用のものでGithub上で一番Starのあるものを選びました。

プラットフォーム

Heroku
- ここで時間を使いたくなかったというのがVPSを借りるなどしなかった理由です。コードを書くことに集中したかったです。あとHerokuの一番下のプランだと無料なのも嬉しいです。

開発の流れ

だいたい以下のような流れで開発をしました。メモしながら開発していたわけではなく記憶を頼りに書いているので結構雑ですが。

準備
開発
  • 最初はReactの公式ドキュメントを読みながらreactのアプリを動くようにしました。最初の時点ではReduxは使っていません。
  • 同じ頃にバックエンドにも簡単なHTTPのエンドポイントを作ってみました。
  • 画面主導で開発を行い、表示する色を選択するための画面右側の部分 → ユーザ入力部分 → 統計情報表示部分 の順に作っていき、必要に応じてバックエンドのAPIも実装していきました。
    • ざっくりこのような部品を作るというのは初めの頃に決めていましたが、細かい部分は開発しながら気付いたときにTODOとしてコード中にメモを残して、頃合いを見て実装していきました。
  • 統計情報表示部分の開発をする頃にstatspropsだけでデータを引きずり回すのが苦しくなってきたので、Redux in Motionで使い方を覚えて導入しました。データを一元管理できるようになって格段に楽になったので、もっと早くやればよかったです。
  • 統計情報表示部分をデモグラフィックでフィルターできるようにしてみました。この機能でサインアップ時に入力した情報が使われています。
  • gomocktestifyを使ってテスト皆無だったバックエンドにテストを書きまくりました。
  • ようやくtravis CIを入れました。Githubにpushしたら自動でテストが流れてデプロイするようになりました。ここまで待たずに最初にやっても良かったように思います。
  • ここまで色の名前を言語(Japanese, Englishなど)に紐づけていましたが、言語というより色カテゴリ(HTML Color, JIS慣用色名など)という上位互換みたいな概念を使ったほうがいいと思ったので、ごっそり書き換えました。

サービスの課題

とりあえず動くものの、課題はまだまだあります。

サービスの外から見えてる部分の課題

  • ユーザが選択できる色の候補が微妙です。特にグレーっぽい色だと使い物になりません。つらい。
  • UI上で色候補を選ぶ作業のUXがだいぶ低いように感じます。僕でもだるいのに、誰がやるのって思います。かなり目が疲れます。
    • 一色ずつ表示してyes/noで答えてもらうみたいな感じのUIがいいかもしれません。ユーザのyes/noに応じて表示する色を賢く変えていくみたいにできれば結構良さそうな気がします。もう少しアイディアが具体的にできたらごっそり書き換えてみたいですね。うまくできれば上下の課題も同時に解決できそうです。
  • PCのGoogle Chromeでしか動作確認をしていません。
    • スマホで色を選択する部分が動かないことは確認しました?
  • Herokuの無料プランなので30分アクセスがないと次のアクセスを処理するのに数秒かかります。

サービスの中身の課題

  • ユーザが選択した色を全てDBに持っていますが、おそらく選んだ色より近い色(どう判断できるかは要定義ですね)は選んだとみなしていい気がします。そういう意味では保存してるデータが冗長な感じです。
  • Reactが賢いのでXSSなどはなさそうですが、悪意のあるポストへの対策が基本的にありません。適当なデータをpostしまくればちゃんとした情報を埋もれさせてしまえます。
  • 統計情報表示画面ではその色に対してSubmitされている情報を全て取得してUIを描画していますが、データ量が増えたらこれではダメそうです。フィルターを変更するごとに通信してサマリー情報を取れるようにしても良さそうです。
  • Reactのコンポーネント設計ではロジックを記述するWrapperコンポーネントとHTMLを出力するコンポーネントを分けるのがお作法のようです。今はそれらを適用したときに比べてメンテナンス性が低い状態だと思います。
  • Reduxのモジュールにはある程度テストを書いてますが、Reactコンポーネントのテストは書けていません。つまり目でチェックしてる?
    • 見た目の部分は他と比べて変化の多い部分でもあり、全部きっちりテストを書いたら逆にメンテナンスが大変そうです。やり方要検討ですね。

というわけで改善の余地はまだまだありすぎますね。

全体を通しての感想

  • Golangが流行っている割にあまり書きやすい感じはしませんでした。機能が少なくて古い言語な印象です。今回必要と思う部分がなくてGoroutineを使わなかったことがGolangを使うメリットを大きく減らしてしまったのかなと思います。
    • Goroutineは動作原理も独特のようですが、並行処理のコード上の表現がJavaで使えるFuture objectを用いたアプローチとかなり異なるので、それがコードの設計にどのように影響するのか見てみたいと思っています。
  • UI上の典型的な表現がreact-toastifyなどのパッケージで大体解決できるという体験が新鮮でした。色んなパッケージを試すのが楽しいです。
  • フロントエンドはバックエンドに比べてデバッグのためのツールが充実してるように感じました。redux-devtools-extensionとかスゴイ。
  • フロントエンドはJavascriptで開発しましたが、Typescriptや全然別の言語からもトランスパイルできる事を途中で知りました。次は静的型付けできる言語を選択してみたいです。
  • 開発中コードを追加するたびにちょくちょくリファクタリングをしていました。おかげで忘れたコードもわりとすぐ理解を取り戻せるという状態で進められました。結構忘れるので読みやすさは生命線です。
  • 散々言われてますが、やはり公式ドキュメントとか解説本とかを読むのがツールの理解に対して近道だと感じました。ツール名でググったときはツールの公式サイトが一番上に出てきてほしいです。
  • 可能な限り毎日触るようにしていたおかげで、モチベーションが上がったときだけやるのではなく、毎日コンスタントに何かをやるということが自分にとって自然になりました。これが一番の収穫だったように思います。

以上、作ったものの紹介と、作ってみた感想でした。

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

React-Hooksはstateよりreducerの方がサンプルとしてわかりやすい気がした

よくカウンターの例として state と setState を見る気がするけど、実は useReducer の方がわかりやすいのでは仮説。

import React from "react";
import ReactDOM from "react-dom";

function useCounter() {
  return React.useReducer((state, i) => state + i, 10);
}

function App() {
  const [count, countUp] = useCounter();
  return (
    <div className="App">
      <h1>count: {count}</h1>
      <h2>
        <button onClick={() => countUp(1)}>count up</button>
        <button onClick={() => countUp(-1)}>count down</button>
      </h2>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

けど、どうなんだろう。

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

gatsby.jsのbuild 時の'TypeError: Cannot read property 'split' of null'エラーの対処

はじめに

gatsby.jsfirebaseでアプリを作成しているのですが、上記のエラーが生じ、「gatsby developではうまくいっているのにgatsby buildになるとうまくいかない、、、なぜだ、、、」となったので今後の為にメモ。

こちらのgithub issueがドンピシャで解決してくれたのでこちらを見ればOKだと思います。

対処方法

ざっくりbefore/after

before

const db = firebase.firestore();
const storage = firebase.storage();
const auth = firebase.auth();

after

let db, storage, auth;

if (typeof window !== 'undefined') {
    db = firebase.firestore();
    storage = firebase.storage();
    auth = firebase.auth();
}

何をしてるのか

読んでコードのごとく、windowオブジェクトの存在をチェックしてfirebaseの各種moduleを呼び出しています。なんでこんなことをするのか。

エラーの原因

gatsby.jsはbuildの際にNode.js環境でhtmlをレンダリングするようです。そのため、window objectが存在しません。なのでwindow objectを明示したコードを記述したりするとbuildがうまくいかなかったりします。

今回の場合はwindow objectを使用しているわけではないのですが、Node.js環境でfirebase.auth()を呼び出していることが原因でした。firebaseのclient side sdkはブラウザー環境で使用されることを前提としているため、Node.js環境はダメでしょ!ってなことみたいです。

gatsby build時のlogでwarning出してくれてました。

Warning: This is a browser-targeted Firebase bundle but it appears it is being 
run in a Node environment.

ちなみに、どこでsplit propertyが呼び出されていてエラーが生じているのかまでは深掘りしていません、、、、

覚えた方が良さげなこと

gatsby.jsのbuildはNode.js環境 !!!!

参考

[v2] TypeError: Cannot read property 'split' of null when building #6668

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

React mapをつかってリストを作る

配列データの要素分のリストを作りたい場合は、JSX内でmapを用いて展開するパターンとrenderメソッド内でmapを用いて展開したリストを変数に格納し、JSX内で展開するパターンがある。

JSX内パターン

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state={
      colors: ["red","orange","black","green","blue","white"]
    }
  }
  render(){
    return(
      <div>
        <ul>
          {this.state.colors.map( c =>
            <li>{c}</li>
          )}
        </ul>
      </div>
    )
  }
}

renderメソッド内パターン

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state={
      colors: ["red","orange","black","green","blue","white"]
    }
  }
  render(){
    const colorsDOM = this.state.colors.map(c => <li>{c}</li>)
    return(
      <div>
        <ul>
          {colorsDOM}
        </ul>
      </div>
    )
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む