20200205のReactに関する記事は10件です。

【動画付き】Next.js の Server Side Rendering (SSR) を理解する。create-react-app と比べてみた。

image.png

Next.jsのサイト、かっこいいですよね ?
クールで、パフォーマンスにも優れていてエンジニアを魅了します。
日本では Nuxt.js が人気のようですが、個人的には Next.js を推しています。

さて、先日 Next.js のチュートリアル を通してサーバサイドレンダリングについて考えさせられる機会がありました。本記事では、そもそもサーバサイドレンダリングのメリットとは?というところから初めて、create-react-app によって実装された SPA と、nextによって実装された SSR ではどのような違いがあるのかを検証してみました。

以下の動画は本記事のサマリーです。
作成したアプリケーションへのリンクも貼っておきます。
右の方がちょっとだけ描画が遅いのがわかりますね。

next react

?(左)next.js で SSR、(右)create-react-app で SPA ?

サーバサイドレンダリング(SSR)とは

main.png

従来の React ベースのアプリケーションの構成を振り返ってみましょう(右図)。この構成の場合、ユーザからのリクエストは、まずはじめに React サーバ(S3 や Netlify)から JavaScript のソースと必要最小限のほとんど空っぽな HTML を返します。それからフロントエンドで HTML 要素をレンダリングする方法をとります。

このようにバックエンド API とフロントエンドの描画を完全に分離する事によって、開発体制を分離した生産性向上や、ユーザに優れた UX を提供できるようになりました。
その一方で、過度なネットワーク通信が発生したり、JavaScript によって生成された Web サイトを検索エンジンのクローラが検知できなくなりました。その結果として、Google の検索項目の上位に自サイトが表示されにくいなどのデメリットも招いてしました。※こちらの記事で紹介されていますが、最近ではあまり問題にならなくなっているようです。

さて、このような問題を解消するためのテクニックがサーバサイドレンダリング(ServerSideRendering)です(左図)。サーバサイドレンダリングは従来フロントエンドで行なっていたレンダリングをバックエンドの Node.js サーバにも移譲しようという考え方です。これにより、モバイル端末がどんなに脆弱でも、ハイパフォーマンスなサーバを使用してレンダリングできます。さらに無駄なネットワーク通信回数も最小限に減らせるでしょう。「バックエンドの Node.js サーバにも」と強調しているのは、フロントエンドでももちろん描画ができる、ということです。初期ページの一部だけはサーバサイドでレンダリングして、残りの要素はフロントエンドからフェッチしてきてレンダリングするといったように用途に応じて使い分けができます。

パフォーマンス

遅いデバイスを使用していると、最初のページのレンダリングに時間がかかり、ユーザ体験が低下します。計算をより強力なサーバーにオフロードすることで、ユーザーが待機する時間を最小限に抑えることができます。
また、サーバーで初期データをプリフェッチしてページを構築すると、サイトを表示するために必要なラウンドトリップの回数が大幅に削減されます。これにより、待ち時間の短縮と帯域幅消費の削減につながります。

fmp.png

SEO 対策

SSR を行なっているサイトは、ページが検索エンジンで簡単にインデックス化されます。クライアント側でルーティング制御を行なっていると、検索エンジンのウェブクロールを遅らせてしまいます。この結果、検索エンジンの上位にリンクを表示することが難しくなります。

Next.js ことはじめ

SSR を理解するために必要最小限の構成で Next.js アプリケーションを組み立てていきます。

必要なライブラリとアプリケーションの実行

$ mkdir next.ssr
$ cd next.ssr
$ yarn init -y

Next.js を最小構成で始めるために必要なライブラリは nextreact, react-dom だけです。早速 yarn でインストールしましょう(npm でもよいですよ)

$ yarn add react react-dom next

package.json には以下の npm scripts を記載しておいて開発を楽に進められるようにしておきましょう。

package.json
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

それぞれのコマンドは以下のように使用します。

  • dev - ローカルでアプリケーションを起動します。
  • build - プロダクション用にアプリケーションをビルドします。
  • start - プロダクション環境でアプリケーションを実行します。

ルーティング

Next.js は非常にシンプルな設計思想でフロント画面が作れるように構成されています。/pages ディレクトリ配下に配置されている js ファイルごとにパスルーティングが行われます。はじめの一歩として /pages/index.jsにファイルを配置して、/という URL で表示できるようにしてみましょう。詳細なドキュメントはこちら

$ mkdir pages
$ touch pages/index.js
pages/index.js
const Index = () => {
  return <h1>Hello World</h1>;
};
export default Index;

ファイルパスと URL パスには以下のような対応関係があります。

ファイルパス URL パス
pages/index.js /
pages/blog/index.js /blog
pages/blog/first-post.js /blog/first-post
pages/dashboard/settings/username.js /dashboard/settings/username
pages/blog/[slug].js /blog/:slug (/blog/hello-world)
pages/[username]/settings.js /:username/settings (/foo/settings)
pages/post/[...all].js /post/* (/post/2020/id/title)

さて、ここまでできれば準備完了です。アプリケーションを起動してみましょう。

$ yarn dev

ブラウザを起動し、/ にアクセスすると画面が表示されるはずです。

image.png

サーバサイドレンダリングの実装

ここから SSR ができるような機能を作っていきましょう。

<Link> コンポートを使用して、他ページに遷移します。以下の例だと /shows/[id] へ遷移させようとしています。また、Next.js には、ページのデータを取得するための標準 API が付属しています。 getInitialProps という非同期関数を使用して実行します。
getInitialProps を使用すると、特定のページのデータをフェッチしてページに渡すことができます。 getInitialProps はサーバーとクライアントの両方で動作します。
この getInitialProps の振る舞いを観測し、SSR を理解していきましょう。

index.js
import Link from "next/link";
import fetch from "isomorphic-unfetch";

const Index = props => (
  <div>
    <h1>Batman TV Shows</h1>
    <ul>
      {props.shows.map(show => (
        <li key={show.id}>
          <Link href="/shows/[id]" as={`/shows/${show.id}`}>
            <a>{show.name}</a>
          </Link>
        </li>
      ))}
    </ul>
  </div>
);

Index.getInitialProps = async function() {
  const res = await fetch("https://api.tvmaze.com/search/shows?q=batman");
  const data = await res.json();
  console.log(`Show data fetched. Count: ${data.length}`);
  return { shows: data.map(entry => entry.show) };
};

export default Index;

pages 配下に /shows/[id].js を配置し、Dynamic Routing ができるようにしておきます。

pages/[id].js
import fetch from "isomorphic-unfetch";

const Post = props => (
  <div>
    <h1>{props.show.name}</h1>
    <p>{props.show.summary.replace(/<[/]?[pb]>/g, "")}</p>
    {props.show.image ? <img src={props.show.image.medium} /> : null}
  </div>
);

Post.getInitialProps = async function(context) {
  const { id } = context.query;
  const res = await fetch(`https://api.tvmaze.com/shows/${id}`);
  const show = await res.json();
  console.log(`Fetched show: ${show.name}`);
  return { show };
};

export default Post;

/ を表示してみましょう。サーバサイドにログ Show data fetched: 10 が表示されるはずです。 index.js をサーバサイドでレンダリングしたという事になりますね。
次にリンクをクリックして /shows/975 に遷移するとブラウザのコンソールにログが表示されてます。これはフロントエンドでデータフェッチとレンダリングが行われたということを意味しています。

ssr.gif

デプロイ

最後にビルドして、デプロイします。ZEIT の now にデプロイします。素晴らしい DX(DeveloperExperimence)です。本当に必要な要素以外全て削ぎ落とした、最高の PaaS だと思ってます。いつも愛用しています。こちらの記事にて丁寧に解説されていました。

bash
$ yarn build # ビルド
$ now        # デプロイ

デプロイしたら動作を確認してパフォーマンスを検証しましょう。Chrome の開発者コンソールを開き、Audit を実行します。

https://batman-tv-shows.geeawa.now.sh/

First Meaningful Paint が 1.0s とでました。まずまずです。

image.png

create-react-app との比較

ここまでできたので Next.js で作成されたアプリケーションと create-react-app で作成されたアプリケーションを比較してみましょう。

以下のようにほぼ同様のソースを使用して、create-react-app アプリケーションを作成します。以下にデプロイしてあります。

https://badman-tv-shows-create-react-app.now.sh/

index.js
import React from "react";
import fetch from "isomorphic-unfetch";

class Index extends React.Component {
  constructor(props) {
    super(props);
    this.state = { shows: [] };
  }
  async componentDidMount() {
    const res = await fetch("https://api.tvmaze.com/search/shows?q=batman");
    const data = await res.json();

    console.log(`Show data fetched. Count: ${data.length}`);

    this.setState({ shows: data.map(entry => entry.show) });
  }

  render() {
    return (
      <div>
        <h1>Batman TV Shows</h1>
        <ul>
          {this.state.shows.map(show => (
            <li key={show.id}>
              <a href="">{show.name}</a>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default Index;

デプロイができたので Audit を実行します。
First Meaningful Paint は 1.4s となり、Next.js によって SSR できるようになったサイトと比較すると少しだけ遅い結果がでました。

image.png

さいごに

今回作成されたアプリケーションは非常にシンプルで、1つの API しか実行しませんし、レンダリングする DOM 要素も少なかったためパフォーマンスにそれほど大きな違いはみられませんでした。それでもアプリケーションが肥大したり、ネットワークの遅い環境、古くて脆弱なモバイルデバイスを使用するとパフォーマンスの違いは顕著になってくるでしょう。SSR の技術は適材適所を見極めて投下していきたいですね。

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

【比較検証】Next.js の Server Side Rendering (SSR) を理解する。create-react-app と比べてみた。

image.png

Next.jsのサイト、かっこいいですよね ?
クールで、パフォーマンスにも優れていてエンジニアを魅了します。
日本では Nuxt.js が人気のようですが、個人的には Next.js を推しています。

さて、先日 Next.js のチュートリアル を通してサーバサイドレンダリングについて考えさせられる機会がありました。本記事では、そもそもサーバサイドレンダリングのメリットとは?というところから初めて、create-react-app によって実装された SPA と、nextによって実装された SSR ではどのような違いがあるのかを検証してみました。

以下の動画は本記事のサマリーです。
作成したアプリケーションへのリンクも貼っておきます。
右の方がちょっとだけ描画が遅いのがわかりますね。

next react

?(左)next.js で SSR、(右)create-react-app で SPA ?

サーバサイドレンダリング(SSR)とは

main.png

従来の React ベースのアプリケーションの構成を振り返ってみましょう(右図)。この構成の場合、ユーザからのリクエストは、まずはじめに React サーバ(S3 や Netlify)から JavaScript のソースと必要最小限のほとんど空っぽな HTML を返します。それからフロントエンドで HTML 要素をレンダリングする方法をとります。

このようにバックエンド API とフロントエンドの描画を完全に分離する事によって、開発体制を分離した生産性向上や、ユーザに優れた UX を提供できるようになりました。
その一方で、過度なネットワーク通信が発生したり、JavaScript によって生成された Web サイトを検索エンジンのクローラが検知できなくなりました。その結果として、Google の検索項目の上位に自サイトが表示されにくいなどのデメリットも招いてしました。※こちらの記事で紹介されていますが、最近ではあまり問題にならなくなっているようです。

さて、このような問題を解消するためのテクニックがサーバサイドレンダリング(ServerSideRendering)です(左図)。サーバサイドレンダリングは従来フロントエンドで行なっていたレンダリングをバックエンドの Node.js サーバにも移譲しようという考え方です。これにより、モバイル端末がどんなに脆弱でも、ハイパフォーマンスなサーバを使用してレンダリングできます。さらに無駄なネットワーク通信回数も最小限に減らせるでしょう。「バックエンドの Node.js サーバにも」と強調しているのは、フロントエンドでももちろん描画ができる、ということです。初期ページの一部だけはサーバサイドでレンダリングして、残りの要素はフロントエンドからフェッチしてきてレンダリングするといったように用途に応じて使い分けができます。

パフォーマンス

遅いデバイスを使用していると、最初のページのレンダリングに時間がかかり、ユーザ体験が低下します。計算をより強力なサーバーにオフロードすることで、ユーザーが待機する時間を最小限に抑えることができます。
また、サーバーで初期データをプリフェッチしてページを構築すると、サイトを表示するために必要なラウンドトリップの回数が大幅に削減されます。これにより、待ち時間の短縮と帯域幅消費の削減につながります。

fmp.png

SEO 対策

SSR を行なっているサイトは、ページが検索エンジンで簡単にインデックス化されます。クライアント側でルーティング制御を行なっていると、検索エンジンのウェブクロールを遅らせてしまいます。この結果、検索エンジンの上位にリンクを表示することが難しくなります。

Next.js ことはじめ

SSR を理解するために必要最小限の構成で Next.js アプリケーションを組み立てていきます。

必要なライブラリとアプリケーションの実行

$ mkdir next.ssr
$ cd next.ssr
$ yarn init -y

Next.js を最小構成で始めるために必要なライブラリは nextreact, react-dom だけです。早速 yarn でインストールしましょう(npm でもよいですよ)

$ yarn add react react-dom next

package.json には以下の npm scripts を記載しておいて開発を楽に進められるようにしておきましょう。

package.json
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

それぞれのコマンドは以下のように使用します。

  • dev - ローカルでアプリケーションを起動します。
  • build - プロダクション用にアプリケーションをビルドします。
  • start - プロダクション環境でアプリケーションを実行します。

ルーティング

Next.js は非常にシンプルな設計思想でフロント画面が作れるように構成されています。/pages ディレクトリ配下に配置されている js ファイルごとにパスルーティングが行われます。はじめの一歩として /pages/index.jsにファイルを配置して、/という URL で表示できるようにしてみましょう。詳細なドキュメントはこちら

$ mkdir pages
$ touch pages/index.js
pages/index.js
const Index = () => {
  return <h1>Hello World</h1>;
};
export default Index;

ファイルパスと URL パスには以下のような対応関係があります。

ファイルパス URL パス
pages/index.js /
pages/blog/index.js /blog
pages/blog/first-post.js /blog/first-post
pages/dashboard/settings/username.js /dashboard/settings/username
pages/blog/[slug].js /blog/:slug (/blog/hello-world)
pages/[username]/settings.js /:username/settings (/foo/settings)
pages/post/[...all].js /post/* (/post/2020/id/title)

さて、ここまでできれば準備完了です。アプリケーションを起動してみましょう。

$ yarn dev

ブラウザを起動し、/ にアクセスすると画面が表示されるはずです。

image.png

サーバサイドレンダリングの実装

ここから SSR ができるような機能を作っていきましょう。

<Link> コンポートを使用して、他ページに遷移します。以下の例だと /shows/[id] へ遷移させようとしています。また、Next.js には、ページのデータを取得するための標準 API が付属しています。 getInitialProps という非同期関数を使用して実行します。
getInitialProps を使用すると、特定のページのデータをフェッチしてページに渡すことができます。 getInitialProps はサーバーとクライアントの両方で動作します。
この getInitialProps の振る舞いを観測し、SSR を理解していきましょう。

index.js
import Link from "next/link";
import fetch from "isomorphic-unfetch";

const Index = props => (
  <div>
    <h1>Batman TV Shows</h1>
    <ul>
      {props.shows.map(show => (
        <li key={show.id}>
          <Link href="/shows/[id]" as={`/shows/${show.id}`}>
            <a>{show.name}</a>
          </Link>
        </li>
      ))}
    </ul>
  </div>
);

Index.getInitialProps = async function() {
  const res = await fetch("https://api.tvmaze.com/search/shows?q=batman");
  const data = await res.json();
  console.log(`Show data fetched. Count: ${data.length}`);
  return { shows: data.map(entry => entry.show) };
};

export default Index;

pages 配下に /shows/[id].js を配置し、Dynamic Routing ができるようにしておきます。

pages/[id].js
import fetch from "isomorphic-unfetch";

const Post = props => (
  <div>
    <h1>{props.show.name}</h1>
    <p>{props.show.summary.replace(/<[/]?[pb]>/g, "")}</p>
    {props.show.image ? <img src={props.show.image.medium} /> : null}
  </div>
);

Post.getInitialProps = async function(context) {
  const { id } = context.query;
  const res = await fetch(`https://api.tvmaze.com/shows/${id}`);
  const show = await res.json();
  console.log(`Fetched show: ${show.name}`);
  return { show };
};

export default Post;

/ を表示してみましょう。サーバサイドにログ Show data fetched: 10 が表示されるはずです。 index.js をサーバサイドでレンダリングしたという事になりますね。
次にリンクをクリックして /shows/975 に遷移するとブラウザのコンソールにログが表示されてます。これはフロントエンドでデータフェッチとレンダリングが行われたということを意味しています。

ssr.gif

デプロイ

最後にビルドして、デプロイします。ZEIT の now にデプロイします。素晴らしい DX(DeveloperExperimence)です。本当に必要な要素以外全て削ぎ落とした、最高の PaaS だと思ってます。いつも愛用しています。こちらの記事にて丁寧に解説されていました。

bash
$ yarn build # ビルド
$ now        # デプロイ

デプロイしたら動作を確認してパフォーマンスを検証しましょう。Chrome の開発者コンソールを開き、Audit を実行します。

https://batman-tv-shows.geeawa.now.sh/

First Meaningful Paint が 1.0s とでました。まずまずです。

image.png

create-react-app との比較

ここまでできたので Next.js で作成されたアプリケーションと create-react-app で作成されたアプリケーションを比較してみましょう。

以下のようにほぼ同様のソースを使用して、create-react-app アプリケーションを作成します。以下にデプロイしてあります。

https://badman-tv-shows-create-react-app.now.sh/

index.js
import React from "react";
import fetch from "isomorphic-unfetch";

class Index extends React.Component {
  constructor(props) {
    super(props);
    this.state = { shows: [] };
  }
  async componentDidMount() {
    const res = await fetch("https://api.tvmaze.com/search/shows?q=batman");
    const data = await res.json();

    console.log(`Show data fetched. Count: ${data.length}`);

    this.setState({ shows: data.map(entry => entry.show) });
  }

  render() {
    return (
      <div>
        <h1>Batman TV Shows</h1>
        <ul>
          {this.state.shows.map(show => (
            <li key={show.id}>
              <a href="">{show.name}</a>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default Index;

デプロイができたので Audit を実行します。
First Meaningful Paint は 1.4s となり、Next.js によって SSR できるようになったサイトと比較すると少しだけ遅い結果がでました。

image.png

さいごに

今回作成されたアプリケーションは非常にシンプルで、1つの API しか実行しませんし、レンダリングする DOM 要素も少なかったためパフォーマンスにそれほど大きな違いはみられませんでした。それでもアプリケーションが肥大したり、ネットワークの遅い環境、古くて脆弱なモバイルデバイスを使用するとパフォーマンスの違いは顕著になってくるでしょう。SSR の技術は適材適所を見極めて投下していきたいですね。

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

Next.jsでCSSを読み込む方法

Next.jsでCSSを読み込むときは、Headを定義しlinkタグにて読み込む

以下のような感じ
linkタグで読み込めばOK

import React from "react";
import Header from "../includes/header";
import Head from "next/head";

const MainLayout = props => {
  return (
    <>
      <Head>
        <title>My Awesome app</title>
        <link
          href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
          rel="stylesheet"
        />
        <link
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
          crossorigin="anonymous"
        />
        <link href="#" rel="stylesheet" />
      </Head>
      <Header /> {props.children}
    </>
  );
};

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

[React]テーブルのセルを結合する

概要

  • Reactで配列をループさせてテーブルを描画する
  • 特定の項目についてセルを結合させたい

作成イメージ

スクリーンショット 2020-02-05 15.01.31.png

  • 共通する項目についてセルを結合する

調べたこと

<table border="3">
  <tr>
    <td rowspan="3">垂直方向の結合</td>
    <td>データ1</td>
  </tr>
  <tr>
    <td>データ2</td>
  </tr>
  <tr>
    <td>データ3</td>
  </tr>
</table>

実装

  • 表示する配列
src/constants/GroupList.js
import { nogizaka, keyakizaka, hinatazaka } from "./Color";

const groupList = [
  {
    id: 1,
    name: "乃木坂46",
    color: nogizaka.color,
    memberList: [
      {
        id: 1,
        name: "齋藤飛鳥",
        age: 21
      }
    ]
  },
  {
    id: 2,
    name: "欅坂46",
    color: keyakizaka.color,
    memberList: [
      {
        id: 1,
        name: "渡邉理佐",
        age: 21
      },
      {
        id: 2,
        name: "小林由依",
        age: 20
      }
    ]
  },
  {
    id: 3,
    name: "日向坂46",
    color: hinatazaka.color,
    memberList: [
      {
        id: 1,
        name: "齊藤京子",
        age: 22
      },
      {
        id: 2,
        name: "小坂菜緒",
        age: 17
      },
      {
        id: 3,
        name: "上村ひなの",
        age: 15
      }
    ]
  }
];

export default groupList;

  • 表示するコンポーネント
src/App.js
import React from "react";
import { Table } from "react-bootstrap";
import styled from "styled-components";
import groupList from "./constants/GroupList";

const ColorTr = styled.tr`
  color: ${({ color }) => color};
`;

function App() {
  return (
    <Table bordered>
      <thead>
        <tr>
          <th>グループ</th>
          <th>名前</th>
          <th>年齢</th>
        </tr>
      </thead>
      <tbody>
        {groupList.map(group =>
          group.memberList.map((member, i) =>
            i === 0 ? (
              <ColorTr key={member.id} color={group.color}>
                <td rowSpan={group.memberList.length}>{group.name}</td>
                <td>{member.name}</td>
                <td>{member.age}</td>
              </ColorTr>
            ) : (
              <ColorTr key={member.id} color={group.color}>
                <td>{member.name}</td>
                <td>{member.age}</td>
              </ColorTr>
            )
          )
        )}
      </tbody>
    </Table>
  );
}

export default App;

行ったこと

  • 配列の個数を結合させる行数としてrowspanの値に設定
src/App.js
<td rowSpan={group.memberList.length}>{group.name}</td>
  • ループさせる配列のインデックスをとり、先頭行の場合(インデックスが0の場合)とそうでない場合を分岐
src/App.js
{groupList.map(group =>
  group.memberList.map((member, i) =>
    i === 0 ? (
      <ColorTr key={member.id} color={group.color}>
        <td rowSpan={group.memberList.length}>{group.name}</td>
      // 省略
      </ColorTr>
    ) : (
      // 省略
    )
  )
)}

終わりに

  • とりあえず実現してみたけど何だか冗長・・・
  • もっと良い方法をご存知の方いましたらコメントください

余談

本題とは全く関係ありませんが、今回使用したカラーコードはそれぞれ以下を参考にしています。

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

React 360でも正規表現を直さないとエラーになります。

先日、この記事を自分用に記載してシェアした訳ですが、
https://qiita.com/Camity/items/f54fdee02d49ca3aa9cd

React VRではなく、最新のReact 360で同様の動作を試してみましたが、
やはり手続き通りにやると同様のエラーが出ました。

下記がReact 360で参照した記事。
https://qiita.com/yushimatenjin/items/870c2b8c965489884571

因みに動作環境は前回と同様Windouwsです。
要するに正規表現の記述が異なる為にエラーが出ます。

An error occurred during the packager process. Exited with code 1.
Look at the packager output above to see what went wrong.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! プロジェクト名@0.0.1 start: node node_modules/react-360/scripts/packager.js
npm ERR! Exit status 1

直すべきファイルの存在する場所は、
C:\~\React360のプロジェクト名\node_modules\metro\src
の下にblacklist.jsというファイルがあるので、その中のvar sharedBlacklistから始まる該当部分を直せば動きます。

もしも、また仕様が変わって、上記のパスにも存在しない場合は、プロジェクト内をblacklist.jsで検索をかけてみると該当ファイルがあると思います。
記述を直す前に一応、コピーを取る事をお勧めします。

var sharedBlacklist = [
/node_modules[/\]react[/\]dist[/\].*/,

/website\/node_modules\/.*/,

/heapCapture\/bundle.js/,

/.\/tests\/./];

↓↓↓↓↓↓↓↓

var sharedBlacklist = [
/node_modules[\/\]react[\/\]dist[\/\].*/,

/website\/node_modules\/.*/,

/heapCapture\/bundle.js/,

/.\/tests\/./];

因みに参照した記事のyarnはインストールをしてないと使えませんので、
npm startとかでnpmで代用できます。

yarnとはに関しては下記参照
https://qiita.com/lelouch99v/items/c97ff951ca31298f3f24

まだ、XRの分野は新しい分野なのかこの手のエラーというか手直しの部分は多いのかもしれません。
みんなが簡単に出来るように、React360に関しても体系的な知識を自分のサイトにいずれまとめる予定です。

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

【React】Font Awesomeを導入するメモ

React.jsのアプリケーションにFont Awesomeを導入する際にいつも忘れて一から調べているので備忘録を残します。
言語はTypeScriptを使ってますのでJavaScriptの方は適宜読み替えてください。

対象

  • Reactアプリ(not React Native)
  • Font Awesome無料版(有料版は使ったことないのでわからない)

パッケージ

インストール必須なもの

npm install @fortawesome/react-fontawesome
npm install @fortawesome/fontawesome-svg-core

使用するアイコンによってインストールするもの

npm install @fortawesome/free-brands-svg-icons
npm install @fortawesome/free-regular-svg-icons
npm install @fortawesome/free-solid-svg-icons

どのパッケージをインストールするかは、下の画像の赤枠で判断できます。
使いたいアイコンをFont Awesomeで調べてください。
twitterIcon.png

使い方

@fortawesome/react-fontawesomeからFontAwesomeIconimport
使いたいアイコンの定義を@fortawesome/free-brands-svg-iconsなどからimportして、FontAwesomeIconiconプロパティに渡します。

import React from 'react';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTwitter } from "@fortawesome/free-brands-svg-icons";
import { faCalendar } from "@fortawesome/free-regular-svg-icons";
import { faCoffee } from "@fortawesome/free-solid-svg-icons";

const App: React.FC = () => {
  const iconStyle: React.CSSProperties = { padding: 10, fontSize: 50 };

  return (
    <div style={{ textAlign: "center", padding: 50 }}>
      <FontAwesomeIcon style={iconStyle} icon={faTwitter} />
      <FontAwesomeIcon style={iconStyle} icon={faCalendar} />
      <FontAwesomeIcon style={iconStyle} icon={faCoffee} />
    </div>
  );
}

result.png

おしまい。

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

NextJS(Typescript)の環境に ESLINT/Prettier をツッコミ CircleCI の設定に追加する

NextJS(Typescript)の環境に ESLINT/Prettier をツッコミ CircleCI の設定に追加する

tslint を利用しようと思いましたが、2019 年に ESLINT に統一するようになるよって話があるのでhttps://github.com/palantir/tslint/issues/4534ESLINT に変更して作成します。

今回のレポですが、環境作成の準備段階として、
- NextJS(Typescript)を now にデプロイする
- Next.jsではじめるTypescript環境
の続きです。

Table of Contents

  1. Prettier と es-lint を追加する
  2. husky を追加して precommit/prepush の前に lint と formatter を走らす
  3. CircleCI の build をする前に lint test を走らす

1. Prettier と ts-lint を追加する

Prettierの有効かをしていきます。

1-1, Prettier を install する

install するのは 2 種類。perttier と pretty-quick を install します。

$ npm install -D prettier pretty-quick

1-2. Prettier の設定ファイルを作成する

$ touch .prettierrc

中身は個人的に基本的な設定のみ対応します。

{
  "semi": true,
  "trailingComma": "all",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2
}

install した pretty-quick が利用できるように、package.json に追加します。

package.json
...
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start",
  "prettier:quick": "pretty-quick --staged"
},
...

試しに実行すると、github 場で変更したファイルを確認して prettier を実行してくれます。

$ npm run prettier:quick
?  Finding changed files since git revision 43aa1b4.
?  Found 0 changed files.
✅  Everything is awesome!

1-3. lint を install する

lint の設定は、lint をインストールして設定ファイルを作成します。

$ npm install -D eslint

1-4. lint の設定をする

es-lint の設定ファイル eslint.json を作成していきます。
--init を利用しファイルを作成していきます。

$ npx eslint --init
? How would you like to use ESLint? To check syntax, find problems, and enforce code
style
? What type of modules does your project use? JavaScript modules (import/export)
? Which framework does your project use? React
? Does your project use TypeScript? Yes
? Where does your code run? Browser
? How would you like to define a style for your project? Use a popular style guide
? Which style guide do you want to follow? Standard: https://github.com/standard/stan
dard
? What format do you want your config file to be in? JSON

作成させた eslintrc は以下になります。

{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": ["plugin:react/recommended", "standard"],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "plugins": ["react", "@typescript-eslint"],
  "rules": {}
}

react を利用する際に error が生じるため、以下の設定を追加します。

eslintrc.json
{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": ["plugin:react/recommended", "standard"],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "plugins": ["react", "@typescript-eslint"],
  "rules": {},
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

lint がコマンドから叩けるように package.json を編集します。

package.json
...
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start",
  "lint": "eslint src/** --ext .ts,.tsx",
  "lint:fix": "npm lint --fix"
},
...

次に prettier の rule を lint の rule にも追加するように設定します。

$ npm install -D eslint-plugin-prettier
eslintrc.json
{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": ["plugin:react/recommended", "standard"],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "plugins": ["react", "@typescript-eslint", "prettier"],
  "rules": {
    "prettier/prettier": "error"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

次にコマンドが正常に動くか確認します。

$ npm run lint

成功したら husky の設定をしていきます。

2. husky を追加して precommit/prepush の前に lint と formatter を走らす

huskyは、git のアクションにフックして、コマンドを走らせることができます。

husky を install して設定していきます。

$ npm install husky --save-dev

今回はコミットの前にフォーマットを走らせ、push の前に lint を走らせるよに変更します。

package.json
"husky": {
  "hooks": {
    "pre-commit": "pretty-quick --staged",
    "pre-push": "npm run lint"
  }
}

3. CircleCI の build をする前に lint test を走らす

circleci に設定を追加してきます。
deploy する前に test をはしらせて、失敗したら deploy しないように設定していきます。

テストの内容を追加していきます。

circleci/config.yml
version: 2.1
executors:
  node:
    working_directory: ~/project
    docker:
      - image: circleci/node:10.12-browsers
## ここから追記
jobs:
  test-lint:
    executor:
      name: node
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: linting code
          command: npm run lint
## ここまで追記

job の作成が完了したら、workflow に追加します。

circleci/config.yml
workflows:
  version: 2
  build-and-cache:
    jobs:
      - build
      - test-lint:
          requires:
            - build
      - deploy-now:
          requires:
            - test-lint

requires で build 後に lint が走るように設定し、deploy については lint を通した後に実装するように変更しました。

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

create-react-appで生成したReactのimportを絶対パスでやる(VSCode対応)

方法

プロジェクトルートに jsconfig.json を生成し、中身を↓にする。
※プロジェクトルート = package.json があるところ

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

src 配下のファイルに対して、↓でimport可能になる。

import Button from 'components/Button';

参考リンク

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

ポートフォリオ作ってみた

  • React
  • GitHub pages
  • Material-Ui

あたりをつかってさくっと作ってみました。

こちらがつくったページ

基本的にはcreate-react-appしてMaterial-Uiのタブを使っただけです。

create-react-appからGitHubPagesにデプロイするのはこのあたりの記事を参考にさせていただきました。

あとは、コンテンツをなんとなくマークダウンで書きたいお気持ちだったので、マークダウンでprofileなどをかいて読み込もうとしたら、Create React AppはまだMarkdownを簡単にLoadする手段がないという記事を発見。
上記の記事に書いてあったraw.macroをつかってマークダウンを読み込むことに成功しました!

const raw = require('raw.macro')
const aboutMe = raw('../data/aboutMe.md')
...
  <ReactMarkdown className="MainPage-contents" source={aboutMe} />
...

読み込んだマークダウンはreact-markdownを使って処理しています。

作ったはいいけど、コンテンツが薄いので、2020年はworksなどを充実させていきたいです。

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

NextJS(Typescript)を now にデプロイする

NextJS(Typescript)を now にデプロイする

これの続きとなります。。。
ので、nextjsの初期設定、circleCIでのプロジェクト作成は簡易的にできているながれですすめたい。

Table of Contents

  1. now、Nextjs とは
  2. now の初期設定をする
  3. nextjs を now に手動デプロイする
  4. nextjs を CircleCI を使って Auto Deploy する

0. now、Nextjs とは

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

nowとは、ZEIT社が提供しているconfigなしでデプロイできるHostToolです。使ってみ思いましたがひくくらい簡単です。

1. now の初期設定をする

nowにアクセスをし、ユーザー登録をおこなっていきます。
下の画面の Deploy Free ボタンをクリックしてアカウント作成に移動します。

c2-zeit-top.png

今回は Contenue With GitHub を使って GitHub でアカウントを作成していきます。

c2-zeit-signup.png

アカウントの作成が完了するとダッシュボードに行くことができます。

c2-zeit-dashboard.png

これで web 上の設定は完了です。
次に、now コマンドを install していきます。

$ npm install -g now

# インストールが完了したら一応バージョンを確認いたします。

$ now --version
16.7.3

コマンドが完了したら、nowコマンドを利用してログインしていきます。
メールアドレスを求められるので、ログインしたアカウントの Email アドレスを利用してログインをしていきます。

$ now login
We sent an email to YOUR_EMAIL_ADDRESS Please follow the steps provided
  inside it and make sure the security code matches Happy Magellanic Penguin.
✔ Email confirmed
> Congratulations! You are now logged in. In order to deploy something, run `now`.

2. nextjs を now に手動デプロイする

$ pwd
/your/project/directory

現状のディレクトリにいることを確認します。
nowでconfigで扱う、now.jsonファイルを作成していきます。

now.json
{
  "name": "next-sample"
}

これで、プロジェクト名を固定してdeployすることができます。

ちなみにプロジェクト名を指定しない場合は、projectのroot directoryの名前が優先されるっぽいです。Gitのレポジトリとかpackage.jsonっぽい感じもしましたが、directory名っぽい。
$ now --prod

Deploying ~/your/project/directory under YOUR_PC
> Using project PROJECT_NAME
> NOTE: To deploy to production (YOUR_PRJECT_NAME.now.sh), run `now --prod`
> Synced 1 file [4s]
> https://XXX.now.sh [4s]
> Ready! Deployed to https:/XXX.now.sh [in clipboard] [24s]

コマンドを入力すると build されてデプロイされます。
表示された URL を確認すると、作成した URL が表記されてるかと思います。

4. nextjs を CircleCI を使って Auto Deploy する

これ書いてから気づいたのですが、PRなどの作成タイミングでauto deployがはしります。www
circleCIをdeployのみで考えている場合は必要ありません。

circleCI での now の設定は3段階。
1.now の token を発行して、circleCI の env に記載。最後に、circleCI の config から参照して deploy する形となります。

4-1. now token を発行する

nowのtokenが作られるタイミングは、loginの際に発行するものと、自分で作成するものがあります。
自分でtokenをの発行する方法は、ダッシュボード画面にいき Settings から token を発行することができます。

dashboardにいき右上のユーザーアイコンから、Settings リンクをクリック。

c2-dashboard-setting.png

setting画面にあるTokenリンクから、とtokenの設定画面に遷移します。

c2-setting-token.png

現状tokenを利用している一覧表示のCreateをクリック

c2-zeit-create-token.png

tokenを利用する名前を設定し、create tokenをクリックするとtokenが表示されます。

c2-zeit-create-token-form.png

c2-zeito-token.png

4-2. CircleCIに Environmentを設定する

nowのtokenの発行がおわったら、CircleCIのEnvironmentに記載していきます。
(Projectがある前提で話をしていきます。)

c2-circleci-project-page.png

設定画面にいったらEnvironment VariablesからAdd Variablesを選択

c2-circleci-setting-environement.png

先ほどCopyしたnow Tokenを入力して、Environmentの設定をします。

c2-circleci-setting-variable-modal.png

これで、.circleci/config.ymlで $NOW_TOKENで登録したtokenを利用できるようになりました。
最後にCIの設定をしていきます。

4-3. CircleCIの設定

設定が完了したら、circleciでデプロイできるよに、.circleci/config.ymlの編集をしていきます。

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
# ここから追記
  deploy-now:
    executor:
      name: node
    steps:
      - checkout
      - run:
          name: update-npm
          command: "sudo npm install -g npm@latest"
      - run:
          name: install now on global
          command: "sudo npm install -g now"
      - attach_workspace:
          at: .
      - run:
          name: deploy to now
          command: now deploy --token $NOW_TOKEN
# ここまで追記
workflows:
  version: 2
  build-and-cache:
    jobs:
      - build
      - deploy-now:
          requires:
            - build

でdeplpyすると、CircleCI経由でdeployすることが可能になります。
nowでauto deployもできるのでこの内容だけだと必要性を感じません。。。
now 自体も色々あるのであとで記載していこうかと。

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