20200204のReactに関する記事は16件です。

Next.jsにてhooksを使いつつgetInitialPropsも使う方法

function.getInitialProps で使える

以下のような感じ。

import React, { useState } from "react";

function Home(props) {

  const [count, setCount] = useState(0);
  console.log(count);

  return (
    <>
      <h1>Home</h1>
    </>
  );
}

Home.getInitialProps = async ({ pathname, query, asPath, req, res }) => {
  return { values: "test" };
};

export default Home;

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

EffectiveなNext.jsを目指して(Routing)

はじめに

Reactを用いてSSR(Server Side Rendering)が可能なフレームワークであるNext.js.簡単にWebアプリケーションを作成することができるが,柔軟性もあり,より効率的に書けるようになれば手軽に高品質なアプリが作れると感じて勉強を始めました.日本語のドキュメントもあまりないので,Next.jsを活用する人たちの役に立てればなと思います.

今回は,Routingについてまとめていきます.サンプルコードとデモページを参考にしながら読んでいただければ,よりわかりやすいかと思います.

環境

  • Next.js - 9.2.1

§1.CSRのルーティングの手段

サンプルコードのsrc/pages/ch1にこの章のコードがあります.

CSRのルーティングの手段として3つの例を紹介します.

  • Link
  • useRouter
  • withRouter

それぞれの例について,サンプルコードを見ていきましょう.

Ex1.Link

src/pages/ch1/index.js(抜粋)
import Link from 'next/link'

// =====================================
// 方法1.Link
const Ex1 = () => {
  return (
    <>
      <Link href='/ch1'>
        <a>§1.CSRでのルーティング</a>
      </Link>
    </>
  )
}

普段使っている<a>タグだとサーバーにリクエストが投げられるので,nextで提供されているLinkというJSXコンポーネントを使います.

Ex2.useRouter

src/pages/ch1/index.js(抜粋)
import { useRouter, withRouter } from 'next/router'

// =====================================
// 方法2.useRouter
const Ex2 = () => {
  const router = useRouter()

  return (
    <>
      <button onClick={() => router.push('/ch1')}>
        §1.CSRでのルーティング(default)
      </button>
      <button onClick={() => router.push('/ch1', '/ch1', { shallow: true })}>
        §1.CSRでのルーティング(shallow: true)
      </button>
    </>
  )
}

<Link>タグだとクライアントからのクリックアクションからしかルーティングができない為,router.push('/ch1')とメソッドでCSRでのルーティングができるようになります.

注目してほしいのは,router.push()の第三引数のshallowというパラメータです.デフォルトではfalseとなっていますが,これをtrueにすることでgetInitialProps()を省いたページルーティングが可能になります.
デモページでブラウザのコンソールを表示した状態でボタンをクリックすると,実際にgetInitialProps()が呼び出されていないことが確認できます.
ch1.PNG

Ex3.withRouter

src/pages/ch1/index.js(抜粋)
import { useRouter, withRouter } from 'next/router'

// =====================================
// 方法3.withRouter
const Comp = ({ router }) => {
  return (
    <>
      <button onClick={() => router.push('/ch1')}>
        §1.CSRでのルーティング(default)
      </button>
      <button onClick={() => router.push('/ch1', '/ch1', { shallow: true })}>
        §1.CSRでのルーティング(shallow: true)
      </button>
    </>
  )
}
const Ex3 = withRouter(Comp)

Ex2と同様にrouterのメソッドでルーティングを制御しています.このrouterの取得の方法で好みの方を選択するといいと思います.

§2.動的ルーティングでは[XXXX].jsシンタックスを使え

Next.jsのルーティングはpages/以下のフォルダとJSファイルがそのままパスとして配置されます.そのため,ブログのidやユーザー名などの動的なルーティングを処理するのがやや面倒で,具体的にはqueryとして値を渡すことでやりくりしています.少し脱線しますが,このqueryを受け取る方法は,getInitialPropsnext/routerを使う方法があります.
具体的には以下のようになります.

  • getInitialProps()
Component.getInitialProps = async ({ query }) => {
  return { query }
}
  • next/router
const router = useRouter()
const { query } = router

getInitialProps()を導入すると,nextはビルド時にjsへと変換し,getInitialProps()がなければhtmlへビルドする仕様になっています.なので,特に理由がなければnext/routerのやり方を採用することをオススメします

さて,本題に戻ります.[XXXX].jsシンタックスとは,pages以下のファイル名を[適当な名前].jsにすることで,そのページの任意の名前に割り当てることができます.詳しい説明は,公式のホームページ(NEXT.js - Dynamic Routes)等を参考にしてください.今回は,[XXXX].jsシンタックスを採用することで,他のやり方とどのような違いがあるかについて説明します.

比較したコードは以下の2パターンになります.

Ex1.hrefにqueryを追加する方法.

src/pages/ch2/index.js(抜粋)
import Link from 'next/link'

// =====================================
// 方法1.hrefにqueryを追加する方法.
const Ex1 = () => {
  return (
    <ul>
      <li>
        <Link
          href={{ pathname: '/ch2/ex1', query: { user: 'hoge' } }}
          as='/ch2/ex1/hoge'
        >
          <a>/ch2/ex1/hoge</a>
        </Link>
      </li>
      <li>
        <Link
          href={{ pathname: '/ch2/ex1', query: { user: 'foo' } }}
          as='/ch2/ex1/foo'
        >
          <a>/ch2/ex1/foo</a>
        </Link>
      </li>
    </ul>
  )
}

Linkのあたりがごちゃごちゃしてます.pages/ch2/ex1/index.js{query: {user: "hoge"}}/ch2/ex1/hogeというパスとして渡しています.

Ex2.[XXXX].jsシンタックスを使用.

src/pages/ch2/index.js(抜粋)
import Link from 'next/link'

// =====================================
// 方法2.[XXXX].jsシンタックスを使用.
const Ex2 = () => {
  return (
    <ul>
      <li>
        <Link href='/ch2/ex2/[user]' as='/ch2/ex2/hoge'>
          <a>/ch2/ex2/hoge</a>
        </Link>
      </li>
      <li>
        <Link href='/ch2/ex2/[user]' as='/ch2/ex2/foo'>
          <a>/ch2/ex2/foo</a>
        </Link>
      </li>
    </ul>
  )
}

Ex1.と比べるとシンプルです.pages/ch2/ex2/[user].js/ch2/ex2/hogeというパスとして渡しています.

動作確認

動作を確認します.
https://next-routing-test.sakoooo1001.now.sh/ch2
を実際に動かすとわかりやすいと思います.

"hrefにqueryを追加する方法"と"[XXXX].jsシンタックスを使用"の/ch2/ex1/hoge/ch2/ex2/hogeをクリックしてみます.
ch2-1.PNG
どちらもqueryを取得できていて,同じ画面が表示されると思います.じゃーコードの量が減るだけだと思いきや,両方のページでこのページをリロードしてみます.すると...
ch2-2.PNG
Ex1.のみが404エラーが返されます.理由は,この ch2/ex1/hogeはクライアント側で無理やり付けたパス名であり,リロードでNextのサーバーにこのパスが渡されたときに対応するファイルが存在しないからです.一方,[XXXX].jsシンタックスを使用すると,サーバーでもNextが自動で割り当ててくれます

よって,動的なルーティングには[XXXX].jsを使いましょう.

Ex3. [...XXXX].jsシンタックス

これは,[XXXX].jsシンタックスの拡張版みたいな感じで,そのページ以下の任意のパスを割り当てて,配列としてqueryを受け取ることができます.動作確認は上のリンクからできます.

§3.asを忘れてはいけない.

一番伝えたかった事になります.§1.でCSRでのルーティング,§2.で[XXXX].jsシンタックスについて説明していきました,この[XXXX].jsシンタックスについて,サンプルコードをもう一度見てみます.

src/pages/ch2/index.js(抜粋)
import Link from 'next/link'

// =====================================
// 方法2.[XXXX].jsシンタックスを使用.
const Ex2 = () => {
  return (
    <ul>
      <li>
        <Link href='/ch2/ex2/[user]' as='/ch2/ex2/hoge'>
          <a>/ch2/ex2/hoge</a>
        </Link>
      </li>
      <li>
        <Link href='/ch2/ex2/[user]' as='/ch2/ex2/foo'>
          <a>/ch2/ex2/foo</a>
        </Link>
      </li>
    </ul>
  )
}

また,

一方,[XXXX].jsシンタックスを使用すると,サーバーでもNextが自動で割り当ててくれます.

と説明しています.つまり,/ch2/ex2/hogeとurlを打てばNextが自動でpages/ch2/ex2/[XXXX].jsに割り当ててくれるわけです.

。。。ん?
じゃ,Linkタグにas要らなくない?と考えてLinkタグを以下のように修正してみます

Bad
<Link href='/ch2/ex2/hoge'>
  <a>/ch2/ex2/hoge</a>
</Link>

このようにして,直接/ch2/ex2/hogeを指定してもnextがpages/ch2/ex2/[XXXX].jsに割り当ててくれるので,大丈夫だろうって魂胆です.実際に確認したサンプルコードを見ていきます.

Ex1. asを消してみた.

src/pages/ch3/index.js(抜粋)
import Link from 'next/link'

// =====================================
// 方法1.asを消してみた.
const Ex1 = () => {
  return (
    <ul>
      <li>
        <Link href='/ch3/ex1/hoge'>
          <a>/ch3/ex1/hoge</a>
        </Link>
      </li>
      <li>
        <Link href='/ch3/ex1/foo'>
          <a>/ch3/ex1/foo</a>
        </Link>
      </li>
    </ul>
  )
}

Ex2. asを残した.

src/pages/ch3/index.js(抜粋)
import Link from 'next/link'

// =====================================
// 方法2.asを残した.
const Ex2 = () => {
  return (
    <ul>
      <li>
        <Link href='/ch3/ex2/[user]' as='/ch3/ex2/hoge'>
          <a>/ch3/ex2/hoge</a>
        </Link>
      </li>
      <li>
        <Link href='/ch3/ex2/[user]' as='/ch3/ex2/foo'>
          <a>/ch3/ex2/foo</a>
        </Link>
      </li>
    </ul>
  )
}

動作確認

動作を確認します.
https://next-routing-test.sakoooo1001.now.sh/ch3
を実際に動かすとわかりやすいと思います.

ブラウザのコンソールを表示してみます.
ch3-1.PNG
いきなりエラーが表示されています.nextのLinkタグは,デフォルトでprefetchtrueになっており,バックグラウンドでリンクのページを事前に取得してくれます.その際に,ch3/ex1/hoge.jsがないよと言っています.

また,両方ともリンクをクリックしてみます.すると,
ch3-2.PNG
ch3-3.PNG
コンソールを見ると,asありでは表示されているChapter3/Ex2のgetInitialPropsだよ: 1というログが,asなしでは観測することができません.ではどこに表示されているのかというと,サーバーのコンソールで表示されています

整理すると,[XXXX].jsシンタックスはサーバーのみで自動で割り当てられます.つまり,asを消して,hrefに動的パスを入力してしまうと,リクエストがサーバーに行きサーバーでパスの割り当て(かつSSR)をしてch3/ex1/hoge.jsを返すことでクライアントで表示できるようになっています.これにより,同じ動作をするのにパフォーマンスに差が出てしまいます

結論としては,

  • hrefにはフォルダpages/のパスを書く

と認識しておくと間違いはないかと思います.

まとめ

Next.jsのRoutingについて見ていきました.次は他の機能について効率化を目指して勉強していきたいと思います.

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

Next.jsのルーティングでは、Linkコンポーネントを使ったほうが良い理由

Next.jsでのルーティング方法

二つあります。

<a href="/about">About us</a>

<Link href="/about">
   <a>About us</a>
</Link>

それぞれの動き

①のaタグでルーティングする場合、リロードしてしまいます。
②のLinkコンポーネントを使うと、リロードしないかつ同じインスタンスで遷移するので、明らかに①より早くなります。

下記のコードを使って試してみてください。
ChromeコンソールのNetworkタブを見れば、明らかに①の方がゴニョゴニョしているのが分かります。

import React, { Component } from "react";
import Link from "next/link";

class Home extends Component {
  render() {
    return (
      <>
        <div>
          <Link href="/">
            <a>Home</a>
          </Link>
          <Link href="/contact">
            <a>Contact</a>
          </Link>
          <Link href="/about">
            <a>About us</a>
          </Link>
          <Link href="/users">
            <a>Users</a>
          </Link>
          <a href="/">Home</a>
          <a href="/contact">Contact</a>
          <a href="/about">About us</a>
          <a href="/users">Users</a>
        </div>
        <h1>Hello</h1>
      </>
    );
  }
}

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

求職者と企業との壁を小さくするWebサービスを2週間ほどで開発しました!

本日、「リクまと」というサービスを公開しました!:tada:

リクまと!
https://rikumato.work

リクまととは

リクまとは採用サイトの掲載を目的にサービスを提供します。
閲覧・検索は無料で誰でも利用することができます。

採用サイトの登録はログインが必要ですが、無料でサービスを受けることができます。

りくまとの開発経緯などはこちらを参照ください
採用サイトのまとめサイト、リクまとをリリースしました!

使用している技術

リクまとはフロントエンドにReactとFirebase、バックエンドにDjangoとGAEを使用しています。
また、デザインにはMaterial UIを利用しました。
短期で開発するためにインフラはなるべく外部ツールに頼ったため、2週間ほどで開発をすることができました。

フロントエンド

ReactとFirebaseは仕事でも使用しており、使い慣れている技術なのでリクまとの開発に選びました。
Firebaseに至っては、認証やドメインの設定が楽なので積極的に使うことにしました。

また、PWA対応をしており、ChromeやSafariなどのブラウザからホーム画面に追加することでネイティブアプリのように操作することができます。

React

環境の立ち上げにはCreate React Appを使用しています。
Create React Appはここ最近プロジェクト立ち上げのデファクトスタンダードになっており、ややこしい設定を必要とせず簡単にReactの環境を構築することができます。

Material UI

画面デザインはMaterial UIを使って実装しました。
Materal UIはReact用のマテリアルデザインコンポーネントであり、機能単位でコンポーネントが提供されています。
ドキュメントが充実しており、カスタマイズに優れているので多くの企業で使われているのを目にします。

公式サイト
Material UI

PWA

Create React Appでは簡単にPWAを導入できる環境が備わっているので、すぐにPWA対応をすることができます。

やり方

src/index.jsに設定されているserviceWorker.unregister();serviceWorker.register();に書き換えます。

次にpublic/manifest.jsonの設定を書き換えます。

manifest.json
{
  "short_name": "リクまと!",
  "name": "リクまと!",
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff",
  "icons": [
    {
      "src": "logo.png",
      "type": "image/png"
    }
  ]
}

最後にpublic/index.htmlにiPhone用に画像パスを追加します。

index.html
...

<link rel="apple-touch-icon" href="%PUBLIC_URL%/app_logo.png" />

...

Firebase

FirebaseではAuthenticationとHostingサービスを利用しました。
Authenticationについては、認証機能をFirebaseに頼ることができるほか、SNS認証も組み込むことができるので時間のかかるログイン機能を実装することなく開発できました。
firebaseui-web-reactというReact用のプラグインもあるので非常に開発が捗りました。

firebaseui-web-react

Googleから提供されているプラグインで、これを使うことで簡単にログイン認証画面が実装できます。
ボタンのデザインも実装済みのものが入っているので、スタイルを調整する手間も省けます。

実際の表示はこのようになっています。
スクリーンショット 2020-02-04 21.02.59.png

公式サイト
FirebaseUI React Components

バックエンド

Djangoは個人的に好きな技術で、以前開発したアプリでも使用した経験がありそこそこ使い慣れていたので選択しました。

詳しくはこちらを参照ください
Djangoで家計簿サービスを開発してリリースするまで 【個人開発】

GAEは軽く仕事で使ったことがある程度ですが、ドキュメントが充実していて開発がしやすそうなイメージだったので利用しています。

Django

フロントエンドとバックエンドをリクまとでは切り離しているため、バックエンドをREST API化してフロントから呼び出せるようにしました。

Django Rest Framework

DjangoでREST APIを開発する際の定番ライブラリです。
簡単にREST APIを構築できるので大変重宝しています。
ただ、クライアントからAPIを呼び出す際は、クロスオリジンに注意する必要があります。

クロスオリジンについてはこちらを参照ください
クロスオリジンリソース共有(CORS)とAccess-Controll-Allow-Originの関係性
CORS (Cross-Origin Resource Sharing) ってなに?

クロスオリジン対策

Djangoには、django-cors-headersという便利なプラグインがあります。
簡単に設定することができます。

setting.py
INSTALLED_APPS = [
    ...
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True

これで、不特定多数のクライアントからアクセスを許可することができます。

サービス公開に際してやっておいたこと

単純にREST APIを作るだけだと、APIに直接ブラウザからアクセスしたときに画面からPOSTができてしまうので、GUI操作画面を表示させない設定をさせました。
また、データが増えると一度にとってくるデータ量も増えてしまうので、ページネージョンをかけられるようにします。
PAGE_SIZEで一度にとってくるデータの数を決められます。
以下の例だと50個取得できることになります。

settings.py
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
    'PAGE_SIZE': 50
}

公式サイト
Django Rest Framework

GAE

GAEでDjango環境はこちらのドキュメントを参考にして構築しました。
ソースをデプロイしたぐらいで特に操作することもありませんでした。

公式ドキュメント
Running Django in the App Engine flexible environment

最後に

もし何か間違いがあれば遠慮なくメッセージをいただければと思います。
とくにDjango周りの実装については自信があまりないので、アドバイスなどいただけれ幸いです。

参考

脱・とりあえず動く[CORS編]
Django REST Frameworkを使って爆速でAPIを実装する
ReactとFirebaseUIで簡単Firebase Authentication
Reactで触るPWA (Progressive Web App)

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

React + Jest + VS Code 環境でESLint の簡易設定をする

はじめに

ESLintとは

公式より、

ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bugs.

コードを一貫性を保ち、バグを防ぐ目的を持った、JSコードのパターンを検知・報告ツール、とのこと。類似のものにJSHintがある。

私はVSCode上でコードの間違いを報告して欲しかったので、初めて設定してみました。VSCode上で自動でリンティングするための最低限の設定になるので、とりあえず簡単に使ってみたい人の参考になればと思います。

最終的なファイル構成は以下の様になります。といっても.eslintrc.jsを足すだけです。

.
├── .eslintrc.js
├── node_modules
├── package-lock.json
├── package.json
├── sample.js
└── tests
    ├── .eslintrc.js
    └── sample.test.js

ESLintのインストールと設定

$ npm install --save-dev eslint

インストールをしたら、トップ階層に設定ファイルを作成する。
($ npx eslint --initコマンドでも設定ファイルを作成可能)

以下の様にES6を有効にして、node環境であることを設定する。

/.eslintrc.js
module.exports = {
    extends: ['eslint:recommended'],
    parserOptions: {
        ecmaVersion: 6,
    },
    env: {
        node: true,
    }
}

※ eslint:recommendedは推奨ルールを全て設定する。
有効になるルールは以下のリンク内でチェックがついているもの。
https://eslint.org/docs/rules/

※ プログラムが動く環境をenvとして定義する

この時点で以下の様に、特定のファイルに対してESLintを実行できる

$ npx eslint <filename>

エラーがなければ何も表示されない。

Jest環境においてESLintを設定する

現状でJestでテストが書かれたファイルに対して、以下の様にLintしようとしても 'describe' is not defined'it' is not definedといったエラーが出ててしまう。

$ npx eslist jest.test.js  // エラーが出る

設定ファイルでenvの設定を追加しても良いが、プラグインがあるのでそれを使う。

$ npm install --save-dev eslint-plugin-jest

冒頭で作成したトップ階層にある.eslintrc.jsに設定を追加することもできなくはないが、テストプログラム以外もそのJestのpluginの対象に入ってしまいあまり好ましくないので、テストプログラム用にtestsフォルダを作成してその配下に新しく.eslintrc.jsを作成する。

tests/.eslintrc.js
module.exports = {
    plugins: ['jest'],
    extends: ['plugin:jest/recommended'],
}

※重要: ESLintは親階層の設定ファイル(.eslintrc.js)も探しに行くので、testsフォルダ配下のコードも、親階層の.eslintrc.js の影響を受ける。

最後にJestで書いたテストプログラムも正しくLintできればOK!

$ npx eslist sample.test.js

VSCodeにESLintを組み込む

現時点だといちいちコマンドを打つ必要があるので、VSCodeで自動でチェックしてくれる様にプラグインを入れる。

VSCodeのExtensionからeslintを検索して、Dirk Baeumerさんのプラグインを入れる。
スクリーンショット 2020-02-04 20.52.25.png

以下の様にテキトーな文字を入れると、エラー表示してくれる。画面左下部にあるところにエラー数も表示されるの便利。
スクリーンショット 2020-02-04 21.01.01.png

(Option) コードタイプ時ではなく、コード保存時にESLintが動く様に設定する

デフォルトだと、文字入力中にESLintが動き赤線が表示されてしまうが、VSCodeのUser setting (画面上部Codeタブ > preferences > setting) からESLintが動くタイミングを変更することができる。

eslint.runの値を onType => onSave に変更することで、ファイル保存後に動くようになる。

スクリーンショット 2020-02-04 21.05.24.png

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

Reactの基本

JSXで変数と関数を使う


const title = "こんにちは世界";
const body = "こちらが本文です";

const returnStrings = () => {
  return "文字列を返す";
};

var reactElement = (
  <div>
    <p>{returnStrings()}</p>
    <p>{Math.random()}</p>
    <h2>{title}</h2>
    <p>{body}</p>
  </div>
);

render(reactElement, document.getElementById("root"));
  • 変数を使う場合 {}で囲う
  • 複数のタグを使う場合、1つのタグで囲う
  • {}の中では関数も実行できる

アローファンクション

function test() {
  console.log("aaa");
}
//これが

const name2 = () => {
  console.log("name2");
};
name2();
//こうなる

引数が1つの場合は()を省略できる

const name3 = val => {
  console.log(val);
};
name3("passed value");


//引数が2つの場合は()をつける
const name4 = (val1, val2) => {
  console.log(val1, val2);
};
name4("passed", "value");

違う書き方(省略形)

const name6 = () => {
  return "returned value";
};
console.log(name6());


const name7 = () => "returned value";
console.log(name7());

const name8 = () => "returned value";
console.log(name8());

アローファンクションを使ってReaceElementを処理する

const returnReactElement = () => 
{
  return <h2>text</h2>;
};

引数を渡す

const returnReactElement4 = (no, name) => {
  const newStrings = `${no}番目は${name}さんです。`;
  return <h2>{newStrings}</h2>;
};

二つの引数を渡して、
渡したものを元に、処理をした内容でReaceElementを返す

no + '番目は' + name + 'さんです。'

//``で囲む
`${no}番目は${name}さんです。`

変数を使って文字列と連結する方法
これをを使うと、+を何度も使わなくていい

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

React+material-uiでコピーボタン付きのテキストボックスを作る

こういうの

textbox.gif

準備

必要なモジュールをインストールします。

  • @material-ui: いい感じのコンポーネントを提供してくれるやつ
  • react-copy-to-clipboard: クリップボードにコピーとかしてくれるやつ

今回はyarnを使用します。
npmを使用している場合は適宜読み替えてください。

$ yarn add @material-ui/core @material-ui/icons react-copy-to-clipboard

今回はTypeScriptを使用するため、react-copy-to-clipboardの型定義ファイルもインストールします。

$ yarn add --dev @types/react-copy-to-clipboard

サンプルコード

import React, { useState } from 'react';
import FormControl     from '@material-ui/core/FormControl';
import OutlinedInput   from '@material-ui/core/OutlinedInput';
import IconButton      from '@material-ui/core/IconButton';
import InputAdornment  from '@material-ui/core/InputAdornment';
import AssignmentIcon  from '@material-ui/icons/Assignment';
import Tooltip         from '@material-ui/core/Tooltip';
import CopyToClipBoard from 'react-copy-to-clipboard';

const TextBoxWithCopyButton: React.FC = () => {
  const [input,   setInput]   = useState<string>('');
  const [openTip, setOpenTip] = useState<boolean>(false);

  const handleChangeText = (e: React.ChangeEvent<HTMLInputElement>): void => {
    setInput(e.target.value);
  };

  const handleCloseTip = (): void => {
    setOpenTip(false);
  };

  const handleClickButton = (): void => {
    setOpenTip(true);
  };

  return (
    <FormControl
      variant='outlined'
    >
      <OutlinedInput
        type='text'
        value={input}
        onChange={handleChangeText}
        endAdornment={
          <InputAdornment position="end">
            <Tooltip
              arrow
              open={openTip}
              onClose={handleCloseTip}
              disableHoverListener
              placement='top'
              title='Copied!'
            >
              <CopyToClipBoard text={input}>
                <IconButton
                  disabled={input === ''}
                  onClick={handleClickButton}
                >
                  <AssignmentIcon />
                </IconButton>
              </CopyToClipBoard>
            </Tooltip>
          </InputAdornment>
        }
      />
    </FormControl>
  );
};

export default TextBoxWithCopyButton;

まとめ

細かい解説は省きます。
Material UI、それなりに綺麗なUIを簡単に作れるので本当に便利ですね。

参考

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

(React + TypeScript)i18nextで名前空間の導入

(React + TypeScript)UseContextとi18nextを使ってi18n対応してみるの続きみたいな感じです。

i18nextを使ったi18n対応で、コンポーネントごとにファイルを分けたいので名前空間を導入します。

名前空間の導入

App.tsx
import { useTranslation, initReactI18next } from 'react-i18next';
import i18n from 'i18next';
import enCommon from './locales/en/common.json';
import jaCommon from './locales/ja/common.json';
import jaNamespace1 from './locales/ja/namespace1.json';
import enNamespace1 from './locales/en/namespace1.json'

i18n.use(initReactI18next).init({
  debug: true,
  resources: {
    en: { common: enCommon },
    ja: { common: jaCommon },
  },
  lng: 'ja',
  fallbackLng: false, 
  returnEmptyString: false, 
});

// 名前空間の追加
i18n.addResources('ja', 'namespace1', jaNamespace1);
i18n.addResources('en', 'namespace1', enNamespace1);

export const App: FC = () => {
  const { i18n } = useTranslation();

  // ...以下省略
}

初期化時にはcommonという名前空間しかありませんでしたが、addResources('locale名', '名前空間(ファイル名)', importしたファイル)という関数を使って別の名前空間を追加できます。

名前空間の使用

Component.tsx
import { useTranslation } from 'react-i18next';

export const Component = () => {
  const { t } = useTranslation('namespace1');
  return(
     <p>{t('テスト1')}</p>
     <p>{t('テスト2')}</p>
  );
}

useTranslation()の引数に名前空間の名前をいれることで、このコンポーネント内ではデフォルトの名前空間として、namespace1.json内で定義されてる単語、文章を出力します。(この例ではテスト1とテスト2がキーになります)

辞書ファイルの自動生成

こちらの記事で紹介されている自動で辞書を生成するbabel-plugin-i18next-extractというライブラリですが、名前空間を自動で検出してくれます(助かるー)。
上記の章のテスト1、テスト2というキーが存在しない場合、自動でテスト1、テスト2というキーを作成してくれます。

package.json
"scripts": {
    "i18next-extract": "NODE_ENV=development babel './src/**/*.{js,jsx,ts,tsx}'"
  },
  "babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "i18next-extract",
        {
          "locales": [
            "ja",
            "en"
          ],
          "outputPath": "./src/locales/{{locale}}/{{ns}}.json"
        }
      ]
    ]
  },
  "dependencies": {
   ...以下省略

ここでoutputPathが出力するjsonファイルになります。
そのため日本語の辞書は、src/locales/ja/namespace1.jsonへ、英語の辞書はsrc/locales/en/namespace1.jsonへ自動的に出力されます。

あとは、yarn i18next-extractするだけです。

疑問

本当は名前空間を導入して、各ページごとに読み込むファイルを分けたかったんですが、うまくいきませんでした。。。
ページごとにi18nを初期化すればできそうだけど、なんか違う様な。。。
詳しい方いらっしゃたら教えてください。

(2020.02.5追記)
一晩寝かせておいたらなんかできました。

ChildComponent.tsx
  useEffect(() => {
    i18n.addResources('ja', 'namespace2', jaNamespace2); 
    i18n.addResources('en', 'namespace2', enNamespace2); 
  },[])

こんな感じでuseEffectで囲うだけ。

参考文献

https://www.i18next.com/overview/api#addresourcebundle
https://i18next-extract.netlify.com/#/configuration

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

Reactのざっくり概要

この記事の目的

公式チュートリアルで三目並べを作ってもらうのが一番理解できるが、その前段階で「React」とはどのようなものなのかを知ってもらうことを目的とする。
本資料を読んだ後に、チュートリアルを両方やってもらったほうがより理解が深まる。

参考資料

ドキュメント

公式ドキュメント

チュートリアル

公式チュートリアル

※余力があれば、以下のチュートリアルも行うことを推奨する
Getting Started with React - An Overview and Walkthrough Tutorial

Reactのざっくり概要

Reactとは何か

Facebookが開発したWebアプリケーションのView(フロントエンド)を作成するためのJavaScriptライブラリ。
公式ページでは以下の特徴を挙げている。

  • 宣言的なView
    • WebアプリケーションのViewをJSX記法で明示する
    • 素のDOMを直接操作しない(Reactが管理する仮想DOMを操作する)
  • コンポーネントベース
    • コンポーネントという単位で部品を作成する
    • コンポーネントでカプセル化を行う
  • 一度学習すれば、どこでも使える
    • フロントエンド
    • React Native(モバイルアプリ開発)

フロントエンドのざっくり歴史

  1. HTMLだけ ※1995年頃
  2. HTML + JavaScript ※1995年〜2000年頃
    • JavaScriptでちょっとした動きを付けた
  3. HTML + Flash ※2000年〜2005年頃
    • JavaScriptはセキュリティ的に危ない!
    • Flashでモリモリ動きを付けた。
    • だけどFlashプラグインが必要。。。
    • iPhoneはFlashを排除(ここで死滅)
  4. HTML + JavaScript ※2005年頃
    • Ajax(非同期通信によるViewの構築)の登場
    • 画面描画に関する処理はクライアントサイド(ブラウザ)、複雑なビジネスロジックやデータの永続化はサーバサイド(WebAPIで機能を提供)という棲み分けが進み始めた
    • ページ遷移しないWebページの登場(SPA)
    • GoogleMap凄い!
  5. HTML + jQuery ※2005年〜2010年頃
    • Ajaxをいい感じにやってくれるライブラリが登場
    • 生DOM操作が辛い。。。
  6. HTML + JavaScriptのMVCフレームワーク(Knockout.jsなど) ※2010年〜2015年頃
    • 生DOM操作の辛さを軽減する方法を模索した時期
    • JavaScript内の値の変更がHTMLに反映されるまでの流れをMVCモデルで解決しようとしてた
    • が、View反映は結局素のDOM操作になるので辛みはそれほど軽減されなかった
  7. JavaScriptの仮想DOMを扱うフレームワーク(React, Vue.js, Angularなど) ※2015年頃〜現在
    • HTMLのDOMツリー構築をJavaScriptが行う
    • 仮想DOMをJavaScriptで操作するので、素のDOMを操作する辛みから開放された
    • 現実的なViewのコンポーネント化に関する手法が提供された

サーバサイドのざっくり歴史

  1. Java + Struts / Spring ※2000年頃
    • サーバサイドでHTMLを構築して、ブラウザでは描画のみさせようという流れ
    • サーバサイドからのレスポンスを待つので、ブラウザ側の描画待ち時間があった(UXが低い)
    • その後、フロントとの棲み分けでAPIサーバに徹する使い方が主流に
  2. Ruby + Ruby on Rails ※2005年頃〜
    • サーバサイドでHTMLを構築することは変わらず。。。
    • その後、フロントとの棲み分けでAPIサーバに徹する使い方が主流に
  3. ASP.NET MVC5 ※2010年頃〜
    • やはり、サーバサイドでHTMLを構築することは変わらず。。。
    • APIサーバの機能に徹する仕組みもあるが、SpringやRailsと比べるとマイナーな存在
  4. Node.js ※2010年頃〜
    • JavaScriptで処理をかけることが特徴。フロントとバックエンドを同じ言語で書ける!
    • JavaScriptの非同期処理を生かして、サーバサイドのI/O待ちをしない(ノンブロッキングI/O)ことで高速なレスポンスを実現
    • この特徴で、C10K問題を解決できる素養がある
    • C10K問題とはクライアント数10000問題のこと。サーバに接続するクライアント数が1万を超えると、サーバのCPUやメモリに余裕があっても並行処理するスレッドが増えすぎてサーバの処理が滞る問題
    • APIサーバとしてのポテンシャルが高いが、サーバサイドは堅牢な言語(型安全であるとか、これまでの長い実績があるとか、技術者がJavaしか書けないとか)が好まれるため、まだ普及しているとは言い難い

ReactとJavaScriptの関係

  • ReactはJavaScriptをいい感じに出力するフレームワーク
    • JavaScriptだけでDOM操作したり部品化するのが辛いので、Reactというフレームワークを使って楽をしようということ
    • Javaに対するStrutsとかSpringの関係と似ている
  • JavaScriptには言語仕様のバージョンがある
    • Javaに7や6といったバージョンがあるように、JavaScriptにもバージョンがある
    • EcmaScriptがJavaScriptの言語仕様であり、JavaScriptとは各メーカーのブラウザのJavaScriptエンジンが実装している実装内容である
    • 各ブラウザによって対応しているEcmaScriptのバージョン(JavaScriptのバージョン)が異なるが、開発者がReactで実装するJavaScriptは最新のバージョンで記述できる(ReactはEcmaScriptのバージョンをダウンコンバートする機能がバンドルされている)

Reactを使うと何が嬉しいのか

素のJavaScript(バニラJS)やjQueryで辛かった以下の問題を解決してくれる。

素のDOMの状態を開発者側が覚えなくても良くなった(仮想DOM)

少し前までは、DOMにidやnameを付与してjQueryなどでDOMを操作していたが、DOMの状態はHTML上にしかなく、JSで書き換えられたDOMがどのような状態になっているかは開発者の脳内で想像するしかなかった。
それに対して、Reactは仮想DOMという仕組みを用いている。Reactは仮想DOM(オブジェクトのインスタンスのようなもの)を操作し、その最終結果をHTMLのDOMに反映するという流れを取る。これによって、開発者はインスタンスの状態でViewの状態を把握できるため、素のDOMを扱うストレスから開放された。

仮想DOMによって再描画が高速になった

素のDOMをidなりnameなりで操作した場合、素のDOMは一部分だけ再描画する機能がないのでDOMツリーを全て再描画することになり描画速度が遅いという問題があった。
それに対して、仮想DOMは仮想DOM内で新旧の差分を取り、差分があったDOMノードのみ生DOMを再描画するので、再描画が高速になった。

画面部品の部品化ができるようになり、再利用性が高まった

HTMLにはファイル分割する現実的な方法がなく(他のHTMLをiframeで読み込む程度)、複数ページで似たようなデザインがあっても各ページで同じ実装をしなければいけなかった。
素のJavaScriptも同様で、比較的新しいEcmaScript6までimportがサポートされておらず、JavaScriptのファイル分割はcommon.js等の外部ライブラリを併用しなければ基本的に不可能だった。

それに対して、Reactはimportによるファイル分割をサポートしているだけでなく、コンポーネントというViewの部品単位で分割(ファイル分割やクラスとしての分割)が可能となり、ファイル分割で1ファイルのステップ数を押さえられるだけでなく、複数画面の同じ画面部品を同じコンポーネントで再利用しやすくなった。

Vue.js、Angularとの比較

現在のフロントエンド・JavaScriptフレームワークはReact, Vue.js, Angularの3つが代表であり、それぞれに以下のような特徴がある。

共通した特徴

  • 仮想DOMを扱う
  • コンポーネント指向である

個別の特徴

これまでの経緯

  • React
    • Facebookが初版を開発
    • 2017年からMITライセンス(OSS)
    • 現在はOSSコミュニティで維持管理
  • Vue.js
    • 個人(Angular開発に関わっていた中国人)が初版を開発
    • MITライセンス(OSS)
    • OSSコミュニティで維持管理
  • Angular
    • Googleが初版を開発
    • MITライセンス(OSS)
    • OSSコミュニティで維持管理

学習コスト

  • React
    • 学習コスト「中」
    • 画面の一部分だけReactを適用することも可能だが、Create React Appで「ReactのWebアプリケーション」として作成するのが一般的
  • Vue.js
    • 学習コスト「低〜中」
    • 画面の一部分にだけVue.jsを適用して動きを付けるjQuery的な使い方が可能
    • ただし、仮想DOMの恩恵を受けるには、Vue cli+シングルファイルコンポーネントを用いてビルドする必要があり、その場合の難易度は「中」となる
  • Angular
    • 学習コスト「高」
    • フルスタックフレームワークなので、Angular wayと呼ばれるアプリケーション構築フレームワークに則る必要があり、DIを使用することが前提であるなど学習コストが高め

フレームワークとしてのトレンド

日本と中国ではVue.jsの人気が上がっているが、世界的に見た場合はReactがデファクトスタンダードである。Angularは世界的に見ても人気度が下がっている。
ライフサイクルが比較的長いシステムで考えた場合、安定したバックグラウンド(Facebookの開発チームが主導)があり、中長期的なサポートが期待できるReactが望ましいと考える。
image.png
出典:Googleトレンド

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

【SharePoint Framework】listen EADDRINUSE: address already in use :::5432 エラー対処

はじめに

開発したWebパーツをgulp serve コマンドを実行して確認しようとした所、以下のエラーが発生しました。

listen EADDRINUSE: address already in use :::5432

原因

前回gulp serveによって実行されたインスタンスが適切に閉じられていない事が原因です。

解決策

以下のコマンドを実行し、再度gulp serveコマンドを実行する事で解決しました。

taskkill /f /im node.exe

参考

Fix For Address Already In Use :::5432: SPFx Development With Gulp
https://www.c-sharpcorner.com/blogs/fix-for-address-already-in-use-5432-sffx-development-wit-gulp

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

styled-components の theme型定義

styled-components の theme、使っていますか?styled-components には、ThemeProviderという、装飾などに関する規定値を提供する仕組みがあります。この様な構成になっているとします(通常 ThemeProvider は SPA ルーティングの上あたりに位置しますが、説明のため簡易な構成としています)

const Component: React.FC = () => (
  <ThemeProvider theme={theme}>
    <RedButton>RedButton</RedButton>
    <GreenButton>GreenButton</GreenButton>
    <BlueButton>BlueButton</BlueButton>
  </ThemeProvider>
)

ThemeProvider に渡している theme は次の様なものです。

export const theme = {
  colors: {
    red: '#f00',
    green: '#0f0',
    blue: '#00f'
  },
  layout: {
    width: 960
  }
} as const

theme は、styled-components コンストラクタ(styled(...)) にラップされた Component props に注入され、Template リテラルから参照するのが通常です。次の例は、Generics どころか型情報もありません。それにも関わらず、この参照props.theme.colors.blueにはキッチリ型推論が効いています。いったいどこから提供されたものでしょうか?

BlueButton.tsx
const StyledComponent = styled(Component)`
  background-color: ${props => props.theme.colors.blue}; /* (property) blue: "#00f" */
`

DefaultTheme 型を宣言結合拡張する

@types/styled-components を確認しましょう(v4.4.2)。DefaultThemeという型定義があります。これは先日の投稿と同じ仕組みを使った、型定義の注入テクニックです。DefaultTheme として用意された空っぽの interface に対し、宣言結合拡張することで、プロジェクト固有の型定義を注入することが出来ます。

import 'styled-components'
// ______________________________________________________
//
export const theme = {
  colors: {
    red: '#f00',
    green: '#0f0',
    blue: '#00f'
  },
  layout: {
    width: 960
  }
} as const
// ______________________________________________________
//
type AppTheme = typeof theme
// ______________________________________________________
//
declare module 'styled-components' {
  interface DefaultTheme extends AppTheme {}
}

今のところ私は遭遇したことがありませんが、ThemeProvider が複数ある場合は、この手法は使えません。ここで紹介した内容は以下のリポジトリで確認できます。

https://github.com/takefumi-yoshii/styled-components-theme-type-definition

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

(React + TypeScript)UseContextとi18nextを使ってi18n対応してみる

下の記事に感化されて、i18n対応をやってみました。

日本語で開発しつつ、いつでも多言語対応できる環境を react-i18next で維持する

i18n対応の部分は上の記事を見ていただく(辞書ファイルの自動生成とかもありますが、この記事ではスキップしてるので是非読んでください)として、useContextを利用してグローバルstateに言語情報を持たせて、パッと切り替えられる様にしました。

トップコンポーネント

まずは大元のApp.tsxにstateを持たせます。

App.tsx
import React, { FC, useEffect, createContext, useReducer } from 'react';
import { appReducer, StateType, ActionType } from './reducer'
import { useTranslation, initReactI18next } from 'react-i18next';
import i18n from 'i18next';
import enJson from './locales/en.json';
import jaJson from './locales/ja.json';
import MainContents from './View/MainContents';

type ContextType = {
  state: StateType;
  dispatch: React.Dispatch<ActionType>;
};

// 初期ステートを作成
const initialState = { lang: 'ja' };

// コンテキストを作成する
export const AppContext = createContext({} as ContextType);

//i18nextの初期化
i18n.use(initReactI18next).init({
  debug: true,
  resources: {
    en: { translation: enJson },
    ja: { translation: jaJson },
  },
  lng: 'ja',
  fallbackLng: false,
  returnEmptyString: false, 
});

const App: FC = () => {
  const { i18n } = useTranslation();
  const [state, dispatch] = useReducer(appReducer, initialState);

  useEffect(() => {
    i18n.changeLanguage(state.lang);
  }, [i18n, state.lang]);

  return (
      <AppContext.Provider value={{ state, dispatch }}>
        <MainContents />
      </AppContext.Provider>
  );
};

Reducer

Reducerは下記の様な感じです。

reducer.ts
export const CHANGE_LANG = 'CHANGE_LANG' as const;

export type StateType = {
  lang: string;
};

export const changeLang = (lang: string) => ({
  type: CHANGE_LANG,
  payload: { lang },
});

export type ActionType = ReturnType<typeof changeLang>;

export const appReducer = (state: StateType, action: ActionType) => {
  switch (action.type) {
    case CHANGE_LANG:
      return { ...state, lang: action.payload.lang };
    default:
      return state;
  }
};

この辺のreducerの書き方はこちらの記事を参考にさせていただきました。

言語切り替えコンポーネント

SubSub.tsx
import React, { useContext } from 'react'
import { AppContext } from '../App';
import { CHANGE_LANG } from '../reducer';

export default const SubSub = () {
  const { state, dispatch } = useContext(AppContext);

  return (
    <button
      onClick={() =>
        dispatch({
          type: CHANGE_LANG,
          payload: { lang: state.lang == 'ja' ? 'en' : 'ja' },
        })
      }
    >
     言語切り替え
   </button>
  );
}

※SubSub.tsxはMain.tsxの孫くらいのコンポーネントだとしてください。Main.tsx配下の子孫コンポーネントであればなんでも良いです。

言語切り替えのところは3カ国以上なら適宜別のロジックでstateを変更してください。

言語が切り替わるコンポーネント

下記は実際に言語が切り替わるコンポーネントです。

AnotherSubSub.tsx
import React from 'react';
import { useTranslation } from 'react-i18next'

export default const AnotherSubSub = () => {
  const { t } = useTranslation();

  return (
    <p>
      {t('ここの言語が切り替わります')}
    </p>
  );
}

こちらもApp以下の子孫コンポーネントでしたら、どこでも構いません。
ここの言語が切り替わりますというのをキーとしてstateにセットしたlocaleに合わせて日本語、英語が切り替えます。

辞書設定

ja.json
{
  "ここの言語が切り替わります": "ここの言語が切り替わります"
}
en.json
{
  "ここの言語が切り替わります": "The language will change right here"
}

これで言語切り替え設定が完了しました。

まとめ

あとは言語の切り替えが発生するコンポーネントに上記の様な設定をすれば、言語切り替えボタンを押した時に全てのコンポーネントに言語切り替えが設定されます。

ただ、initReactI18nextを初期化する時に全ての辞書ファイルを読み込むので、辞書ファイルが非常に大きい場合はパフォーマンスが落ちるかもしれません(この辺は未検証)。

また、初期化時に常に日本語で表示しているため、そこは適宜初期の言語設定を加えてください。(アメリカからアクセスあった場合は初期設定で英語にする等)

(日本語をキーにするのは便利だなぁ)

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

【個人開発】GIF生成アプリを作ってアメリカのProduct Huntに投稿した話

背景

こんにちは。
キャリアチェンジをしてエンジニアとして就職するためにバンクーバーの専門学校に通っているMarieと申します。

現在、実績なしで海外就職という難易度高めの問題に立ち向かっています。
もうすぐ就職しなくてはならないのですが、なんせ実績がないので、ポートフォリオに作品を載せて、求人サイトでポチポチ応募するだけでは十分なアピールにはなりません。

だったら、もっと自分から動かなくては!!と思い、作ったアプリをアメリカのProduct Huntに投稿してみました!というキラキラ(と見せかけてとても泥臭い)お話です。

Product Huntってなに

Product Huntは、シリコンバレーで生まれたサービスで、世界中のユーザーが新サービスを投稿したり、探したりできるサービスです。
投稿されたサービスに対して、ユーザーが票を入れる(UPVOTEする)仕組みがあり、デイリーランキングとして集計されるのも魅力の1つです。
見ているだけで楽しいので、まだ知らない方は覗いてみると良いと思います。

何を作ったか

MsgifというGIF生成アプリを作りました。
どんなサービスなのかは画像を見てもらった方が早いと思うのですが、タイピングした文字がGIFアニメーションに変換されるというものです。
messagif (17).gif

どんな風に作ったか

技術としてはReact.js、ライブラリはstateを管理するためにRedux、HTMLをCanvasに変換するためにhtml2canvas、Canvasで撮った画像をGIFに変換するためにjsgifを使っています。



仕組みとしては、テキストエリアに文字をタイプする度にキャプチャーして画像に変換し、それを配列に放り込んで、文字を打ち終わったタイミング(CreateGIFというボタンが押されたタイミング)でそれらを1つのGIFファイルにまとめて出力しています。

もしコードに興味がある方は、GitHubに公開していますので、こちらをご覧ください。

投稿してみてどうだったか

実は、このエントリーを書いている時点で、Productのデイリーランキングの集計真っ最中です!

1日でランキングがリセットされてしまうため、前日の0時前後にパソコンの前に座って、「行けー!!!」と力強くエンターキーを打刻。
投下した直後から反応は悪くなかったのですが、なんせ無名で実績もゼロなので、放っておいて票が集まるわけがありません。

Twitter、Facebook、LinkedIn、WhatsApp、LINE…いろんなところでペコペコしながら清き一票をお願いして、現時点でUpvotes:140、ランキング10位の状態です。
スクリーンショット 2020-02-03 18.42.36.png



個人開発をしていて思うのは、必要なスキルがめちゃくちゃ多くて割と無理ゲーだということです。
コーディングができるだけでは十分ではなく、どうやったら人に見てもらえるか考えるマーケティングの力も要りますし、サービスを知ってもらう営業努力もかかせません。

今日1日だけで色んなところにペコペコしすぎて、とても疲れ…充実していました。
まだ集計中なので、需要がありそうであれば後日結果もご報告できたらなと思います。

結局宣伝かい

はい...すみません!
いろんなところでペコペコしておいて、このままQiitaでペコペコしないわけにはいきません。
自分としては1年前にはReactなんて触ったこともなかったので、正直サービスをリリースできて感慨深いものがあります。
ユーザーの皆様にはそんなことは関係ないのは百も承知ですが、
もしサービスが面白いと思っていただけたら、1票を入れてUpvoteをしていただけると大変励みになります!!
(Upvoteするにはログインが必要なのでお手数だとは思うのですが…何卒よろしくお願いします?‍♀️

▼投票はこちらから
https://www.producthunt.com/posts/msgif

長くなりましたが、自分で考えたサービスが世の中の人に使われるというのは本当に嬉しいことです。
多くの人がこのサービスを面白がってくれたら幸いです!


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

2020年にフロントエンドを勉強する人が作るべきたったひとつのアプリ

最近ではReactやVueを使ったリッチでインターラクティブなUIがどんどん主流になってきていますし、2020年以降もこの流れは加速し続けるでしょう。

SPA(Single Page Application)やPWA(Progressive Web Application)の普及によって今までモバイルでしかできなかったことがwebでもどんどんできるようになってきています。

また、Firebaseを使うことでクラアントサイドだけの高速なサービス開発が可能になってきていて、今後ますますフロントエンドのニーズは増えるのは確実です。

(サーバーサイドが必要ないという主張がしたいのではありませんが)

フロントエンドをどのように勉強するのか

初心者に立ちはだかる壁

しかし、何か作ってみようと思ってもなかなかほどよいアプリがありません。TODOぐらい簡単なものだと雰囲気を掴むのにはちょうどいいですが、TODO程度のアプリケーションでは実務レベルに通用する知識は身につきません。

面接に「TODOアプリを作りました!」と持っていっても、「それだけ?」と突き返されてしまうでしょう。

逆に、大規模なものを作ろうとすると工数がかかりすぎてすぐに挫折してしまいます。フロントエンドは思ってるより工数がかかることが往々にしてあります。

設計がしっかりとしていないとバグまみれになってしまうので、いきなり大きすぎるアプリケーションに挑戦するのはおすすめできません。

残念ながらrailsチュートリアルのように、体系的にフロントエンドを学べる無料の教材もないのです。

progateで学べることもせいぜい基礎の基礎ぐらいです。

Googleカレンダーというソリューション

では、いい勉強方法はないのか。そんなことはもちろんありません。

レベルアップを積めつつ挫折しない程度のアプリがあります。それがGoogleカレンダーです。

この記事ではなぜGoogleカレンダーがちょうど勉強にいいレベルなのかを紹介していきます。

以下のようなアプリを作成しました。

overview.gif

デモサイト

実装にはReact/Reduxを用いていますが、各々の勉強してみたい言語に置き換えて読んでください。

Googleカレンダーを一人で作れるようになったのであれば、脱フロントエンド初心者と胸を張って言えるでしょう!

※ デモサイトではデータの永続化はしていません。

なぜGoogleカレンダーなのか

ほどよいコンポーネント量(7つ)

├── AddScheduleDialog
│   ├── container.jsx
│   ├── presentation.jsx
│   └── style.css
├── CalendarBoard
│   ├── container.jsx
│   ├── presentation.jsx
│   └── style.css
├── CalendarElement
│   ├── index.jsx
│   └── style.css
├── CurrentScheduleDialog
│   ├── container.jsx
│   ├── presentation.jsx
│   └── style.css
├── ErrorSnackBar
│   ├── container.jsx
│   └── presentation.jsx
├── Navigation
│   ├── container.jsx
│   └── presentation.jsx
└── Schedule
    ├── index.jsx
    └── style.css

僕が実際に作ってみたときはコンポーネントは7つでした。

スクリーンショット 2020-01-26 15.50.08.png

このような構成でコンポーネントを作成しました。残り3つはdialogなどなのでこのコンポーネントツリーとは別に構築して、reduxがある状態になったときにrenderされるようにしました。

流石に一個のファイルで済ますには大きすぎますし、適当に分けすぎるとギリギリ何かを実装しようとしたときにめちゃくちゃ面倒なバグが発生しうるぐらいのサイズ感なのです。

それほどコンポーネントの量が多くないのでレイヤー分けはしていません。

しかし、もしMaterial Design系のライブラリを使わなかった場合はかなりのUIパーツが必要になるので、Atomic Designなどのレイヤー分けが必要になると思います。かなり大変にはなるとは思いますが、それはそれでとてもいい設計の練習になるでしょう。

程よいreduxの状態の量(4つ)

.
├── addSchedule
│   ├── actions.js
│   └── reducer.js
├── calendar
│   ├── actions.js
│   └── reducer.js
├── currentSchedule
│   ├── actions.js
│   └── reducer.js
├── rootReducer.js
└── schedules
    ├── actions.js
    ├── effects.js
    └── reducer.js

自分でreduxのアプリを作ってみるとなかなか3個以上にはならず、複雑な状態管理でないのでreduxの恩恵を理解できません。

しかし、4つぐらいになってくると、コンポーネントで管理するにはかなりつらいのでreduxで作る意味理解できるようになってきます。どう分割してどう組み合わせるかを考える必要があるという意味でカレンダーはかなりおすすめです。

たとえば、予定は日付と一緒に管理するかどうか、カレンダーは日付の配列をreduxのstateにするのかそれとも年月だけを持っておくのか、、、などかなりたくさんのことを考える必要がありました。

フロント側のロジックが多く工夫する必要がある

いざフロントのアプリを作ろうと思っても、気づいたらほとんど情報を表示できるだけのアプリになっています。フォームが1つだけでその値によって表示が変わるぐらいのアプリになってしまいがちです。

たくさんのフォームを作成すれば複雑にできますが、フロントの勉強のためにかなり大掛かりなサーバーを用意する必要があり現実的ではありません。

それにインタラクティブさの不要なフォームが個人開発レベルではほとんどでしょう。そのようなフォームであればHTMLだけで実装できるのでReactを使う意味がありません。

しかし、Googleカレンダーを作るとなると話は変わってきます。

サーバー側の実装は最低限(予定を管理するCRUDのAPIサーバーのみ)で十分ですし、データを永続化をしないフロントだけの実装でもかなり複雑になります。

というのも、カレンダーそのものを一から作る必要があるのと、カレンダーの配列とその月の配列をうまく合わせて表示させる必要があるからです。そしてこれらをうまくやってくれてかつMaterial Designに準拠したライブラリはないのです。

共通のロジックを分離したりロジックをどこに書くかという設計を検討する必要がありかなりいい訓練になります。僕は、serviceというディレクトリを作ってそこにロジックを記述するようにしました。

UIライブラリの使い方を勉強できる

「UIライブラリを使うのは甘えだ!」という考えの方もいらっしゃるかもしれません。

しかし、既存のライブラリを使って爆速で一定以上のクオリティのものを作るという訓練が積めるのでむしろ使った方が成長が見込めます。

MaterialUIはドキュメントもしっかり揃っているのでドキュメントを読む練習にもなります。

image.png

「UIライブラリの」と書きましたが、ライブラリ全般の使い方を学ぶことができるのはかなりのメリットです。

日付の扱いをマスターできる

カレンダーを作成する上で日付ライブラリはマストです。大抵のアプリケーションだと、日付のフォーマットぐらいしか使うことがありませんが、カレンダーを作るとなるとそうはいきません。

ライブラリの提供している機能をフル活用してカレンダーのロジックを作る必要があります。

const createCalendar = month => {
  const firstDay = getMonth(month);
  const firstDayIndex = firstDay.day();

  return Array(35)
    .fill(0)
    .map((_, i) => {
      const diffFromFirstDay = i - firstDayIndex;
      const day = firstDay.add(diffFromFirstDay, "day");

      return day;
    });
};

例えば、上記のコードは日付の配列を作成するためのものです。カレンダーは前後の月を表示する必要になってくるので、日付の演算をしてカレンダーを生成するのがもっとも楽です。うまく配列を操って目的の出力を得る必要があるので、アルゴリズム的にも考えて実装する必要があります。

僕も実際にカレンダーを作ってみて初めて日付の演算などを使いとてもいい勉強になりました。

APIの実装を盛り込みやすい

フロントだけで完結させることもできますが、せっかくならサーバー側も実装して非同期処理を盛り込んだ方がより現場に近いプロダクトに仕上げることができます。

先ほども述べたように、API側は予定の管理ぐらいしかやることがないので実装がとても楽です。

最低限のAPIを実装したリポジトリを用意したので、よかったら使ってください!APIの仕様書も書いてあります。

https://github.com/Dragon-taro/calender-app

dbの接続が切れた時のリトライが実装できていないのでPRお待ちしております。。w

追加できる機能がたくさんある

TODOアプリやブログアプリだと思いつくことが限られていますが、Googleカレンダーなら無限に作り込み要素があります。

そして本家からアイデアをもらえばいいだけなので何を実装したらいいかやUIを考えるコストがかかりません。

追加できそうな機能を以下にあげてみます。

  • 予定の編集
  • ログイン・認証
  • 予定の共有
  • 表示の変更(月・週・日)

これを全部実装できるようになっている頃にはきっと現場の第一線で活躍できているでしょう。

【宣伝】 Googleカレンダーを作るチュートリアル

Googleカレンダーが勉強にいいのはわかったけど、どやったら作れるのかわからない。。難しそう。。そんな方も多いのではないでしょうか?

フロントの開発経験が2年程度ある僕も実際に実装してみたのですが、設計を考えるところや細かなUIの作り込みがかなり難しかったです。

そこで、、、

React/ReduxでGoogleカレンダーを作成できるチュートリアルを作成しました!!

作るだけでも難しいのにそれをさらに説明するというのはとても大変でした。。

かなり気合いを入れて説明しているので大ボリュームですが、その分かなり勉強になることを保証します。

もちろんこの記事で説明したGoogleカレンダーを作ることで学べることはすべて教材の中で説明しています。

これを最後までやり切れば自分で作りたいと思ったアプリケーションを作れるようになっていることでしょう!!

この教材はTechpitというマーケットで作成させていただきました。下記の画像のように学習に適したUIとなっているので、noteのチュートリアルなどと比べてかなり見やすくなっています。

また、運営側がレビューや動作確認もしてくださっているので、かなり品質の高い教材となっています。

ぜひ、この教材でReactでのフロント開発をマスターしてください!

教材はこちら

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

2020年にWebフロントエンドを勉強する人が作るべきたったひとつのアプリ

最近ではReactやVueを使ったリッチでインターラクティブなUIがどんどん主流になってきていますし、2020年以降もこの流れは加速し続けるでしょう。

SPA(Single Page Application)やPWA(Progressive Web Application)の普及によって今までモバイルでしかできなかったことがwebでもどんどんできるようになってきています。

また、Firebaseを使うことでクラアントサイドだけの高速なサービス開発が可能になってきていて、今後ますますWebフロントエンドのニーズは増えるのは確実です。

(サーバーサイドが必要ないという主張がしたいのではありませんが)

Webフロントエンドをどのように勉強するのか

初心者に立ちはだかる壁

しかし、何か作ってみようと思ってもなかなかほどよいアプリがありません。TODOぐらい簡単なものだと雰囲気を掴むのにはちょうどいいですが、TODO程度のアプリケーションでは実務レベルに通用する知識は身につきません。

面接に「TODOアプリを作りました!」と持っていっても、「それだけ?」と突き返されてしまうでしょう。

逆に、大規模なものを作ろうとすると工数がかかりすぎてすぐに挫折してしまいます。Webフロントは思ってるより工数がかかることが往々にしてあります。

設計がしっかりとしていないとバグまみれになってしまうので、いきなり大きすぎるアプリケーションに挑戦するのはおすすめできません。

残念ながらrailsチュートリアルのように、体系的にWebフロントを学べる無料の教材もないのです。

progateで学べることもせいぜい基礎の基礎ぐらいです。

Googleカレンダーというソリューション

では、いい勉強方法はないのか。そんなことはもちろんありません。

レベルアップを積めつつ挫折しない程度のアプリがあります。それがGoogleカレンダーです。

この記事ではなぜGoogleカレンダーがちょうど勉強にいいレベルなのかを紹介していきます。

以下のようなアプリを作成しました。

overview.gif

デモサイト

実装にはReact/Reduxを用いていますが、各々の勉強してみたい言語に置き換えて読んでください。

Googleカレンダーを一人で作れるようになったのであれば、脱Webフロント初心者と胸を張って言えるでしょう!

※ デモサイトではデータの永続化はしていません。

なぜGoogleカレンダーなのか

ほどよいコンポーネント量(7つ)

├── AddScheduleDialog
│   ├── container.jsx
│   ├── presentation.jsx
│   └── style.css
├── CalendarBoard
│   ├── container.jsx
│   ├── presentation.jsx
│   └── style.css
├── CalendarElement
│   ├── index.jsx
│   └── style.css
├── CurrentScheduleDialog
│   ├── container.jsx
│   ├── presentation.jsx
│   └── style.css
├── ErrorSnackBar
│   ├── container.jsx
│   └── presentation.jsx
├── Navigation
│   ├── container.jsx
│   └── presentation.jsx
└── Schedule
    ├── index.jsx
    └── style.css

僕が実際に作ってみたときはコンポーネントは7つでした。

スクリーンショット 2020-01-26 15.50.08.png

このような構成でコンポーネントを作成しました。残り3つはdialogなどなのでこのコンポーネントツリーとは別に構築して、reduxがある状態になったときにrenderされるようにしました。

流石に一個のファイルで済ますには大きすぎますし、適当に分けすぎるとギリギリ何かを実装しようとしたときにめちゃくちゃ面倒なバグが発生しうるぐらいのサイズ感なのです。

それほどコンポーネントの量が多くないのでレイヤー分けはしていません。

しかし、もしMaterial Design系のライブラリを使わなかった場合はかなりのUIパーツが必要になるので、Atomic Designなどのレイヤー分けが必要になると思います。かなり大変にはなるとは思いますが、それはそれでとてもいい設計の練習になるでしょう。

程よいreduxの状態の量(4つ)

.
├── addSchedule
│   ├── actions.js
│   └── reducer.js
├── calendar
│   ├── actions.js
│   └── reducer.js
├── currentSchedule
│   ├── actions.js
│   └── reducer.js
├── rootReducer.js
└── schedules
    ├── actions.js
    ├── effects.js
    └── reducer.js

自分でreduxのアプリを作ってみるとなかなか3個以上にはならず、複雑な状態管理でないのでreduxの恩恵を理解できません。

しかし、4つぐらいになってくると、コンポーネントで管理するにはかなりつらいのでreduxで作る意味理解できるようになってきます。どう分割してどう組み合わせるかを考える必要があるという意味でカレンダーはかなりおすすめです。

たとえば、予定は日付と一緒に管理するかどうか、カレンダーは日付の配列をreduxのstateにするのかそれとも年月だけを持っておくのか、、、などかなりたくさんのことを考える必要がありました。

クライアント側のロジックが多く工夫する必要がある

いざWebフロントのアプリを作ろうと思っても、気づいたらほとんど情報を表示できるだけのアプリになっています。フォームが1つだけでその値によって表示が変わるぐらいのアプリになってしまいがちです。

たくさんのフォームを作成すれば複雑にできますが、Webフロントの勉強のためにかなり大掛かりなサーバーを用意する必要があり現実的ではありません。

それにインタラクティブさの不要なフォームが個人開発レベルではほとんどでしょう。そのようなフォームであればHTMLだけで実装できるのでReactを使う意味がありません。

しかし、Googleカレンダーを作るとなると話は変わってきます。

サーバー側の実装は最低限(予定を管理するCRUDのAPIサーバーのみ)で十分ですし、データを永続化をしないクライアントだけの実装でもかなり複雑になります。

というのも、カレンダーそのものを一から作る必要があるのと、カレンダーの配列とその月の配列をうまく合わせて表示させる必要があるからです。そしてこれらをうまくやってくれてかつMaterial Designに準拠したライブラリはないのです。

共通のロジックを分離したりロジックをどこに書くかという設計を検討する必要がありかなりいい訓練になります。僕は、serviceというディレクトリを作ってそこにロジックを記述するようにしました。

UIライブラリの使い方を勉強できる

「UIライブラリを使うのは甘えだ!」という考えの方もいらっしゃるかもしれません。

しかし、既存のライブラリを使って爆速で一定以上のクオリティのものを作るという訓練が積めるのでむしろ使った方が成長が見込めます。

MaterialUIはドキュメントもしっかり揃っているのでドキュメントを読む練習にもなります。

image.png

「UIライブラリの」と書きましたが、ライブラリ全般の使い方を学ぶことができるのはかなりのメリットです。

日付の扱いをマスターできる

カレンダーを作成する上で日付ライブラリはマストです。大抵のアプリケーションだと、日付のフォーマットぐらいしか使うことがありませんが、カレンダーを作るとなるとそうはいきません。

ライブラリの提供している機能をフル活用してカレンダーのロジックを作る必要があります。

const createCalendar = month => {
  const firstDay = getMonth(month);
  const firstDayIndex = firstDay.day();

  return Array(35)
    .fill(0)
    .map((_, i) => {
      const diffFromFirstDay = i - firstDayIndex;
      const day = firstDay.add(diffFromFirstDay, "day");

      return day;
    });
};

例えば、上記のコードは日付の配列を作成するためのものです。カレンダーは前後の月を表示する必要になってくるので、日付の演算をしてカレンダーを生成するのがもっとも楽です。うまく配列を操って目的の出力を得る必要があるので、アルゴリズム的にも考えて実装する必要があります。

僕も実際にカレンダーを作ってみて初めて日付の演算などを使いとてもいい勉強になりました。

APIの実装を盛り込みやすい

クライアントだけで完結させることもできますが、せっかくならサーバー側も実装して非同期処理を盛り込んだ方がより現場に近いプロダクトに仕上げることができます。

先ほども述べたように、API側は予定の管理ぐらいしかやることがないので実装がとても楽です。

最低限のAPIを実装したリポジトリを用意したので、よかったら使ってください!APIの仕様書も書いてあります。

https://github.com/Dragon-taro/calender-app

dbの接続が切れた時のリトライが実装できていないのでPRお待ちしております。。w

追加できる機能がたくさんある

TODOアプリやブログアプリだと思いつくことが限られていますが、Googleカレンダーなら無限に作り込み要素があります。

そして本家からアイデアをもらえばいいだけなので何を実装したらいいかやUIを考えるコストがかかりません。

追加できそうな機能を以下にあげてみます。

  • 予定の編集
  • ログイン・認証
  • 予定の共有
  • 表示の変更(月・週・日)

これを全部実装できるようになっている頃にはきっと現場の第一線で活躍できているでしょう。

ちょっと宣伝

React/ReduxでGoogleカレンダーを作成できるチュートリアルを作成しました!!

自分で一から挑戦した方が力はつきますが、「一気にレベルアップしたい!」といった人や「何から手をつけていいか分からない」という人は、このチュートリアルをぜひ検討してみてください。

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

Next.jsではじめるTypescript環境

NextJSではじめるTypescript環境

Table of Contents

0 Next.jsとは
1. Next.jsの開発環境作成
2. Typescriptの有効化
3. CircleCIでbuildを実行

0. Next.jsとは

Zeit社が提供しているReactを用いたSSR(SST)のOpen Source Projectです。
Vueで言うところのNuxtくらいの認識です。

Next js - The React Framework.png

1.Next.jsの開発環境作成

プロジェクトとpackage.jsonを作成していきます。

$ mkdir next-sample && cd next-sample
$ npm init -y
{
  "name": "next-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

次に、ReactとNextJSを依存関係に追加していきます。

$ npm install react react-dom next

パッケージが保存されると、package.jsonは以下のように更新されます。

package.json
{
  "name": "next-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^9.2.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}

次にnextのコマンドが使えるように、package.jsonを編集します。

package.json
{
  "name": "next-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
// ここから
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  },
// ここまで編集
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^9.2.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}

Nextjsで表示するためにroutingを追加していきます。
Nextjsのroutingはpagesというディレクトリを作成すると勝手にroutingが生成されます。

$ mkdir pages
$ npm run dev
> next-sample@1.0.0 dev /your/project/next-sample
> next

[ wait ]  starting the development server ...
[ info ]  waiting on http://localhost:3000 ...
[ ready ] compiled successfully - ready on http://localhost:3000
[ wait ]  compiling ...
[ ready ] compiled successfully - ready on http://localhost:3000

pages内部に何も作成していないため、404ページが以下のように表示されますが、localhost:3000を確認すると以下のような表示になります。

404.png

routingが正常に行われるかの確認のため、pages/index.jsxを追加していきます。

$ touch pages/index.jsx
pages/index.jsx
import React from 'react'

export default () => (<div>Hello World</div>)

作成後、localhost:3000を再読み込みさせると以下のページが表示されれば完璧です。

localhost-3000.png

2. Typescriptの有効化

次に作成したnext-sampleへTypescriptの有効化をしていきます。

$ npm install -D typescript @types/react @types/react-dom @types/node
$ touch tsconfig.json

作成したtsconfig.jsonを加筆していきます。
注意して欲しいのはincludeプロパティにsrc/*/と追加したため、typescriptはsrc以下にあるファイルしか読み込まないように設定しました。

tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "esnext",
    "target": "es6",
    "jsx": "preserve",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "moduleResolution": "node"
  },
  "include": ["src/**/*"], 
  "exclude": ["node_modules"]
}

次に、srcフォルダを作成しpagesをsrc以下に移動させます。

$ mkdir src
$ mv pages src
$ npm run dev

npm run devを動かすと、typescriptファイルコンパイルされ、先ほどと同じ以下の画面が表示されれば、あとはjsxファイルをtsxに変更します。

localhost-3000.png

ファイルの変更は以下の感じです。

$ mv src/pages/index.jsx src/pages/index.tsx

ファイルを変更し、もうサイドdevコマンドをうちうえと同じ画面が表示されればTypescript化は完了です。

$ npm run dev

このあとは、自分好みにtsconfigを変更したり下に書いたcircleCiの設定などを変更したり遊んでください。

3. CircleCIでbuildを実行

特段CirclCIを使う必要性はないのですが、とりあえず使いたいなーと考えている方がいましたら、コピペとかで初期設定をしてみてください。

circleciはCI/CDツールです。
一例でいうと、githubにpushしたブランチをそのままデプロイしたり、testを回したりなどできるCI/CDツールです。
今回は何もしないですが、初期設定を記載しておきます。

$ mkdir .circleci
$ touch .circleci/config.yml

config.ymlは以下のようにbuildまでが正常に行われるかの設定のみ記載いたします。

circleci/config.yml
version: 2.1
executors:
  node:
    working_directory: ~/project
    docker:
      - image: circleci/node:10.12-browsers
jobs:
  build:
    executor:
      name: node
    steps:
      - checkout
      - run:
          name: update-npm
          command: "sudo npm install -g npm@latest"
      - restore_cache:
          key: node-{{ .Branch }}-{{ checksum "package-lock.json" }}
      - run:
          name: npm install on ci
          command: npm ci
      - save_cache:
          key: node-{{ .Branch }}-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - persist_to_workspace:
          root: .
          paths:
            - .
      - run:
          name: build flat
          command: npm run build
workflows:
  version: 2
  build-and-cache:
    jobs:
      - build

参考までに、うえの実装をレポにしておきました。
Nextjsのサンプルレポジトリ

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