20200722のReactに関する記事は7件です。

初めてのGoogle Maps API by React

はじめに

30代未経験からエンジニア転職をめざすコーディング初学者のYNと申します。お読みいただきありがとうございます。
ReactとGoogle mapをつかったアプリを作成した際、初学者が参考となる資料が思いの外少なかったので、本記事を投稿いたしました。
本記事では、2020/7月現在、最もアクティブかつ使いやすいと思われる@react-google-maps/apiを使用しています。
このライブラリは、Google Maps API機能を広くカバーしており、Reactコンポーネントとして提供してくれているのでとても便利です。感謝。

今回ご紹介する機能

  • Mapコンポーネントの描画
  • マーカーと情報の表示
  • マーカーアイコンのクラスター表示
  • 多地点を回る際の最適ルートを表示

事前準備

  • GoogleMapのAPIキーを取得
  • Map関連のGoogleAPI(今回はMaps JavaScript APIDirections API)を有効化

Mapの描画

基本となるGoogleMapを描画するMapコンポーネントを作ります。
Snazzy Mapsからカスタムデザインをインポートすることができます。
スクリーンショット 2020-07-22 21.24.11.png

GoogleMapComponent.js
import React, { useCallback, useRef } from "react";
import { GoogleMap, useLoadScript } from "@react-google-maps/api";

import mapStyles from "./mapUtils/mapStyles";
// 地図のデザインを指定することができます。
// デザインは https://snazzymaps.com からインポートすることができます。

const libraries = ["places"];
const mapContainerStyle = {
  height: "60vh",
  width: "100%",
};
// 地図の大きさを指定します。

const options = {
  styles: mapStyles,
  disableDefaultUI: true,
  // デフォルトUI(衛星写真オプションなど)をキャンセルします。
  zoomControl: true,
};

export default function GoogleMapComponent() {
  const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: process.env.REACT_APP_googleMapsApiKey,
    // ここにAPIキーを入力します。今回は.envに保存しています。
    libraries,
  });

  const mapRef = useRef();
  const onMapLoad = useCallback((map) => {
    mapRef.current = map;
  }, []);
  //API読み込み後に再レンダーを引き起こさないため、useStateを使わず、useRefとuseCallbackを使っています。

  if (loadError) return "Error";
  if (!isLoaded) return "Loading...";

  return (
      <GoogleMap
        id="map"
        mapContainerStyle={mapContainerStyle}
        zoom={8}
     // デフォルトズーム倍率を指定します。
        center={{
          lat: 43.048225,
          lng: 141.49701,
        }}
     // 札幌周辺にデフォルトのセンターを指定しました。
        options={options}
        onLoad={onMapLoad}
      >
      </GoogleMap>
  );
}

マーカーと情報の表示

クリックなどのイベントに合わせてアイコンや場所情報を表示するMarkerコンポーネントを作ります。
images.jpeg

GoogleMapComponent.js
import PlaceInfo from "./PlaceInfo";
export default function GoogleMapComponent() {
  return (
      <GoogleMap>
        <PlaceInfo/>
      </GoogleMap>
  );
}

PlaceInfo.js
import React, { useState } from "react";
import { Marker, InfoWindow } from "@react-google-maps/api";

export default function PlaceInfo() {
  const places = [
    { info: "info1", location: { lat: 43.048225, lng: 141.49701 } },
    { info: "info2", location: { lat: 44.048225, lng: 142.49701 } },
  ];

  const [selected, setSelected] = useState(null);

  return (
    <>
      {places.map((marker) => (
        <Marker
          key={`${marker.location.lat * marker.location.lng}`}
          position={{
            lat: marker.location.lat,
            lng: marker.location.lng,
          }}
          onMouseOver={() => {
            setSelected(marker);
            // マウスオーバーで<InfoWindow>が描画されます。
          }}
          icon={{
            url: "url of icon",
            origin: new window.google.maps.Point(0, 0),
            anchor: new window.google.maps.Point(15, 15),
            scaledSize: new window.google.maps.Size(30, 30),
            // ここでアイコン表示の設定ができます。
          }}
        />
      ))}

      {selected ? (
        // MarkerにマウスオーバーされたときにInfoWindowが表示されます。
        <InfoWindow
          position={{
            lat: selected.location.lat,
            lng: selected.location.lng,
          }}
          onCloseClick={() => {
            setSelected(null);
          }}
        >
          <div>{selected.info}</div>
        </InfoWindow>
      ) : null}
    </>
  );
}


マーカーアイコンのクラスター表示

マーカーアイコンをいくつも表示するとアイコン同士が重なって見えにくくなってしまいます。
それを回避するために、地図の表示倍率にしたがってクラスター表示することができます。
今回はクラスター表示にカスタムアイコンを使用する方法も併せて紹介します。
images (1).jpeg

GoogleMapComponent.js
import CustomMarkerClusterer from "./CustomMarkerClusterer";
export default function GoogleMapComponent() {
  return (
      <GoogleMap>
        <CustomMarkerClusterer/>
      </GoogleMap>
  );
}

CustomMarkerClusterer.js
import React from "react";
import { Marker, MarkerClusterer } from "@react-google-maps/api";

export default function CustomMarkerClusterer() {
  const places = [
    { info: "info1", location: { lat: 43.048225, lng: 141.49701 } },
    { info: "info2", location: { lat: 44.048225, lng: 142.49701 } },
  ];

  const clusterStyles = [
    {
      textColor: "white",
      url: "path/to/smallclusterimage.png",
      height: 50,
      width: 50,
    },
    {
      textColor: "white",
      url: "path/to/mediumclusterimage.png",
      height: 50,
      width: 50,
    },
    {
      textColor: "white",
      url: "path/to/largeclusterimage.png",
      height: 50,
      width: 50,
    },
  ]);
  // クラスターサイズに応じてカスタムアイコンで表示することができます。

  const options = {
    gridSize: 50,
    styles: clusterStyles,
    maxZoom: 15,
  };

  return (
    <MarkerClusterer options={options}>
      {(clusterer) =>
        places.map(
          (marker) =>
            selectedActivities[marker.activityClass] && (
              <Marker
                key={`${marker.location.lat * marker.location.lng}`}
                clusterer={clusterer}
                position={{
                  lat: marker.location.lat,
                  lng: marker.location.lng,
                }}
                icon={{
                  url: "url of icon",
                  origin: new window.google.maps.Point(0, 0),
                  anchor: new window.google.maps.Point(15, 15),
                  scaledSize: new window.google.maps.Size(30, 30),
                }}
              />
            )
        )
      }
    </MarkerClusterer>
  );
}


多地点を回る際の最適ルートを表示

Directions APIを使うことで、2地点を結ぶ最短ルートだけでなく、多地点を結ぶ最短ルートを表示することができます。
今回は、始点と終点と経由地を指定することで、経由地の順序を最適化してルートを表示する方法を紹介します。
スクリーンショット 2020-07-22 21.21.55.png

GoogleMapComponent.js
import Direction from "./Direction";
export default function GoogleMapComponent() {
  return (
      <GoogleMap>
        <Direction/>
      </GoogleMap>
  );
}

Direction.js
import React, { useState, useCallback } from "react";
import { DirectionsRenderer, DirectionsService } from "@react-google-maps/api";

export default function Direction() {
  const origin = { lat: 42.755955, lng: 141.32816 };
  // 始点を指定する
  const destination = { lat: 45.299023, lng: 141.65308 };
  // 終点を指定する
  const transitPoints = [
    {
      location: { lat: 43.66406, lng: 142.85445 },
      stopover: true,
    },
    { location: { lat: 43.906742, lng: 144.79872 } },
    { location: { lat: 43.286533, lng: 143.18524 } },
  ];
  // 経由地を(順不同で)指定する

  const [currentDirection, setCurrentDirection] = useState(null);
  // ここにDirectionsServiceへのAPIコールで得られたルート情報を保存する

  const directionsCallback = useCallback((googleResponse) => {
    if (googleResponse) {
      if (currentDirection) {
        if (
          googleResponse.status === "OK" &&
          googleResponse.geocoded_waypoints.length !==
            currentDirection.geocoded_waypoints.length
        ) {
          console.log("ルートが変更されたのでstateを更新する");
          setCurrentDirection(googleResponse);
        } else {
          console.log("前回と同じルートのためstateを更新しない");
        }
      } else {
        if (googleResponse.status === "OK") {
          console.log("初めてルートが設定されたため、stateを更新する");
          setCurrentDirection(googleResponse);
        } else {
          console.log("前回と同じルートのためstateを更新しない");
        }
      }
    }
  });
  // (1) DirectionsServiceコンポーネントはレンダーされるとルート検索し、結果をcallbackとして返す。
  // (2) このAPIレスポンスを今回のようにstateに保存すると、stateが変わったことにより、DirecitonsServiceコンポーネントが再度レンダーされる。
  // (3) DirectionsServiceコンポーネントがレンダーされると再度APIコールを行う。
  // 上記(1)~(3)の無限ループを防ぐため、(3)の結果がstateと変わらなければstateを更新しない、という処理を上記に実装した

  return (
    <>
      <DirectionsService
        options={{
          origin,
          destination,
          travelMode: "DRIVING",
          // 走行モードを指定する。今回は自動車に設定
          optimizeWaypoints: true,
          // 経由地の順序を最適化する場合はtrueに設定する
          waypoints: transitPoints,
        }}
        callback={directionsCallback}
      />
      {currentDirection !== null && (
        <DirectionsRenderer
          options={{
            directions: currentDirection,
          }}
        />
        // DirectionsServiceのAPI検索の結果としてcurrenctDirectionがあれば、その結果をDirectionsRendererで表示する。
        // 予めルート情報を持っていれば、DirecitonsServiceでAPIコールする必要はない。
      )}
    </>
  );
}


最後に

冒頭に申し上げた通り、@react-google-maps/api はGoogle Maps API機能を広くカバーしており、Reactコンポーネントとして提供してくれているのでとても便利です。
今回ご紹介した以外にも様々な機能があるので、ReactをつかってGoogleMapを実装したい方にはマストなライブラリだと思いました。

参考サイト

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

Reactの紹介

はじめに

仕事や趣味の開発でReactを使用してみたので簡単に紹介してみようと思います。

Reactとは

  • ReactはWebアプリケーションのUIを作るためのライブラリ(※フレームワークではない)
  • Facebookがオープンソースとして公開している
  • FacebookやNetflixやAtlassianなど色々なサイトが利用している

Reactを使うメリット

パフォーマンスが良い

DOMとは

DOMはページを表示する際に作られるもの、ブラウザはこれをもとにページを描画している
パフォーマンスが良い(DOMとは).png
※jQueryはこのDOMを操作している

既存の仕組みが遅い理由

既存の仕組みだと受け取った情報を元に毎回DOMを1から構築し、ブラウザに描画しているため遅い
パフォーマンスが良い(既存の仕組みが遅い理由) .png

Reactの仕組み

Reactでは先に仮想DOMを作成し、仮想DOMからDOMを作成する仕組みになっている
※最初に仮想DOMを作っているのが大事
パフォーマンスが良い(Reactの仕組み) .png

仮想DOMとは

実際のDOMと対になるもので、DOMよりはやく作ることができるように設計されたもの
※HTMLを構築する木構造データでJavascript内でDOMを仮想的に作っているらしいです
パフォーマンスが良い(仮想DOMとは).png

画面が更新されるまでの流れ

Reactでは画面に更新があった際、1つ前の仮想DOMと差分を確認し、差分だけDOMに反映させる
パフォーマンスが良い(画面が更新されるまでの流れ) .png

jQueryより見やすい

人によるかもだけど項目の出し分けなどはjQueryよりReactの方が見やすいと思います
例えば以下はボタンを押すとpタグが表示されるコードをjQueryで書いた例になります。
jQueryより見やすい.png
これをReactで書くと以下のようになると思います。これはクラスで書いてありますが、関数で書くともう少しシンプルになると思います。
jQueryより見やすい2.png

プロジェクトを作るのが簡単

プロジェクトの作成は「npm」で提供されている「create-react-app」コマンドを使えるようにしておけば、1コマンドで作れる。
このコマンドが出るまでは自分でディレクトリを作る必要があった模様。
プロジェクトを作るのが簡単.png

プロジェクトにNodeJSのサーバーが含めれているので、デバッグが楽

ReactのプロジェクトにはNodeJSサーバーが入っているので「npm start」とコマンドを叩くだけで動作確認をすることができる。
NodeJSサーバーが立ち上がっている状態で編集を加えると自動でコンパイルされ画面に反映される。
image.png

記法が間違っている場合はコンパイル時にエラーとなる

NodeJSサーバーが立ち上がっている状態で間違った記法で書いて保存するとエラーが表示される
image.png

書き方がパーツ単位のため再利用しやすい

Reactでは画面のパーツ(一覧とか)を別のファイルに定義することができるのでヘッダーなど画面ごとに定義する必要がなく再利用がしやすい。
書き方がパーツ単位のため再利用しやすい.png

ビルドするとNodeJSが入っていない環境でも動く

ビルドすると動かすのに必要なJSファイル等が生成されるため、NodeJSが入っていない環境でも動く
image.png

AndoridアプリやIOSアプリも作ることができる(ReactNative)

別途ライブラリ(react-native)を入れる必要があるが、Reactの記述方法でアプリを作ることもできる
アプリを作る際、動作確認をする度に毎回コンパイル等の作業が発生するがReactNativeの場合は保存したタイミングでデバック用のスマホに反映されるので楽
image.png

Reactを使うデメリット

デプロイする際はapache等のドキュメントルートをプロジェクトのエンドポイントに指定する必要がある

Reactはビルドを行うとドキュメントルートに配置する前提でパスを生成するため、サブディレクトリ等に配置するとJSやCSSなどが404となってしまうので注意!
image.png
※サブディレクトリに配置する場合は別途ライブラリが必要

MVC的な書き方はできない

Reactはあくまで画面を作るライブラリなのでMVC的な書き方をすることができない
※データを取りに行く場合はAPIから取得するような形になる
MVC的な書き方はできない.png

JSXがわかりにくい

JSXとはReactでHTML(DOM)を出力するための独自構文のこと
他の言語と違ってhtmlとかがそのまま代入できたりするので気持ち悪いと思う人もいる模様
JSXがわかりにくい.png

まとめ

  • 簡単な静的ページ(htmlが1枚のページとか)を作るならjQuery、動的なページを作るならReactの方が向いてそう
  • MVC形式で作るならReactは向いていない(フレームワークによっては埋め込めるものあるらしい)
  • 高速なページを作るなら仮想DOMが使われているReactの方が良さそう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

create-rewact-appでの作成時の、eslintのunable to resolve path to module への対応

.eslintrcに下記のように追記する。特に何かをimoportする必要はない。

module.exports = {
  ...,
  settings: {
    'import/resolver': {
      node: {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        paths: ['./src']
      }
    }
  },
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

create-react-appでの作成時の、eslintのunable to resolve path to module への対応

.eslintrcに下記のように追記する。特に何かをimportする必要はない。

module.exports = {
  ...,
  settings: {
    'import/resolver': {
      node: {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        paths: ['./src']
      }
    }
  },
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React で Markdown を HTML に変換しつつ簡単に syntax highlight したい

使うもの

実装例

~/components/CodeBlock.tsx
import React from "react"
// syntax highlight の styleはお好み、自分は JetBrains 信者なので darcula 推し
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { darcula } from "react-syntax-highlighter/dist/cjs/styles/prism"

interface P {
  value: string
  language?: string
}

const CodeBlock: React.FC<P> = ({ language, value }) => {
  return (
    <SyntaxHighlighter language={language} style={darcula}>
      {value}
    </SyntaxHighlighter>
  )
}

export default CodeBlock
~/pages/hoge
// 呼び出し側、どこかのページとか任意の場所で
import React from "react"
import ReactMarkdown from 'react-markdown'
import CodeBlock from "~/components/CodeBlock"

interface P {
  // マークダウンで記述された内容をstringとして受け取るとする
  markdown: string
}

const Page: React.FC = ({ markdown }) => {
  // 先に用意した CodeBlock コンポーネントを renderers に渡すと
  // md から html に変換する際、code タグになる部分だけ渡したコンポーネントを使って描画してくれるので
  // syntax highlight が簡単にできる
  return (
    <ReactMarkdown
      source={markdown}
      renderers={{ code: CodeBlock }}
    />
  )
}

style の import 元は 2 種類あるので注意

// CommonJS 準拠
import { darcula } from "react-syntax-highlighter/dist/cjs/styles/prism"
// ECMAScript 準拠
import { darcula } from "react-syntax-highlighter/dist/esm/styles/prism"

ES6構文のexportに対応してない環境だと esm から import してしまうとエラーとなるので、環境に合わせて esmcjs か選択する。

他に使えるオプションとかはそれぞれの README に書いてた。


ブログに書いたものコピペです

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

Reactチュートリアルをこなしてみてその1

Reactチュートリアルをこなしてみてその1

公式チュートリアルをやってみたのでアウトプットします。

Reactのコンポーネントについて

class ShoppingList extends React.Component {
    render() {
        return {
            <div className="shopping-list">
                <h1>Shopping List for {this.props.name}</h1>
                <ul>
                    <li>Instagram</li>
                    <li>WhatsApp</li>
                    <li>Oculus</li>
                </ul>
            </div>
        }
    }
}

ReactはJSXという書式を使って書いていきます。
上記のコードを見ればわかるようにJavascriptでもHTMLのように書けるような書式といった感じです。
上記のようなコードひとまとまりを指して、Reactではコンポーネントと呼ぶそうです。
ShopppingListはいわゆるクラスに相当する部分ですね。
つまり、コンポーネントはReact.Componentを継承したクラスを指すという見方もできます。
このコンポーネントにいろいろ書いてそれをrender()メソッドでうまいことやったあと、returnで描画したいHTMLを返すというのがReactの基本みたいです。
ちなみにreturn以下のHTML部分はJavascriptで書くと

return React.createElement('div', {className: 'shopping-list'},
  React.createElement('h1', /* ... h1 children ... */),
  React.createElement('ul', /* ... ul children ... */)
);

上記のようになるそうです。
JSXのがわかりやすいですね。
単なるHTMLとはclassclassNameになっているところや<ShoppingList />と書けばそれ全体を指定することもできます。
例えば<div />としてやれば、divタグすべてが指定されます。

Propsについて

では公式チュートリアルのように三目並べを作っていきます。
この三目並べは

  • Square
  • Board
  • Game

の3つのコンポーネントから構成されます。
実はこの3つのコンポーネントは親子関係ができていたりするのですが、それは少しずつ見ていくとしてまずは盤面を作ってみます。
そのためのコードが以下の部分です。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

class Square extends React.Component {
    render() {
        return (
            <button className="square">{this.props.value}</button>
        );
    }
}

class Board extends React.Component {
    renderSquare(i) {
        return <Square value={i} />;
    }

    render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

現時点ではSquareがマス単位、Boardが盤面全体に対してのコンポーネントだと思ってください。
ここで重要なのはrenderSquareメソッドでSquareに対して値(value={i})をパスしているところです。
少し複雑なのですが要は

renderSquare(i) { return <Square value={i} />;}

この部分が

{this.renderSquare(i)}

として描画されることになるのですが、そのためにまずrenderSquareメソッドでSquareコンポーネントに対して各マスの値はvalue={i}だよという指示を出します。
すると、Squareコンポーネント側は{this.props.value}でそれを受け取ります。
Reactではこのようにあるコンポーネントからコンポーネントへと情報を流していくように書いていくのですが、そこでパスされていくのがpropsということになるわけですね。
あとはSquareコンポーネントで各マスはボタンだよという定義がなされているので{this.renderSquare(i)}の各部分は0~9のマスのボタンとして描画されるということです。
また、流れでさらっと書いてますがBoardは盤面全体を表すということで上記のような過程を踏んでいます。
つまり、Squareコンポーネントでマスがどういうものなのか(今回はボタン)を定義して、それをBoardで具体的な盤面として表しているという感じです。
よって、BoardはSquareに対して親の関係にあたるということになります。

stateについて

では、次にSquare コンポーネントがクリックされた場合に “X” と表示されるように変えてみます。

class Square extends React.Component {
 render() {
   return (
     <button className="square" onClick={() => alert('click')}>
       {this.props.value}
     </button>
   );
 }
}

SquareコンポーネントのbuttonタグにonClickプロパティを追加し、その引数として関数を設定しています。
この場合、マスをクリックしたらアラートがポップアップするということになりますね。
これでアクションを設定したので、あとはSquareコンポーネントがこのアクションを記憶できるように処理を書いていきます。
Reactではそのような処理を定義するのにstateというものを使います。
では書いてみましょう。

class Square extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

  render() {
    return (
      <button className="square" onClick={() => this.setState({value: 'X'})}>
        {this.state.value}
      </button>
    );
  }

}

まずコンストラクタを追加します。
コンストラクタは初期化やプリセットの値を用意するために定義するものですが、今回はstateの状態を初期化するために用意することになります。
次にrender()メソッド側も手を入れます。
まず、onClickプロパティの引数をthis.setState()に変更します。
setStateはstateを変更するためのメソッドです。
つまり、現状ではBoardから受け取ったpropsをコンストラクタで引き取りthis.stateにvalueを初期化して代入したあとに、Squareのrender()メソッドにおいてそれを書き換えているという処理になっています。
これで、どのマス目をクリックしてもXマークが表示されることになります。

リファクタリングしていく

基本の処理ができたところでここからはリファクタリングを行って三目並べを完成させていきます。
Reactではこのあと行うState のリフトアップのように実装→リファクタリングを繰り返していくような流れでコードを書いていくのが基本的な流れになるみたいです。

Stateのリフトアップ

さて、先述の通り現在はSquareコンポーネントでstateの状態が管理されています。
しかし、ゲームが今どういう状態なのかということはより上位のコンポーネントで管理をしたい方が都合がいいでしょう。
今回の場合は、BoardでStateを管理し、各Squareにpropsを渡すことでどのマスに何を描画するかを決定するという状態にしたいわけです。
ということでSquareからBoardへとStateの管理を移譲していきます。
では見ていきましょう。

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state= {
      squares: Array(9).fill(null),
    };
  }

    renderSquare(i) {
      return <Square value={this.state.squares[i]} />;
  }
}

まず、constructorをBoardコンポーネントに移します。
ここからBoardでvalueとして0~8の値を渡したあと、SquareのStateでXマークに値を書き換えていたのをBoardから直接個別の値を渡すようにしたいのでrenderSquare()を上記のようにします。
次に、ここからonClickの挙動を変更します。
今、マス目に何が入っているかの管理をBoardコンポーネントに移しましたが、各マスにそれを反映させるためにはSquareがBoardに移したstateを更新できるようにしなければいけません。
しかし、stateはいわゆるprivateなものなので、コンポーネント外からの直接の変更は受け付けません。
なので、BoardからSquareのonClickにstateを変更するような関数を渡して、クリックのアクションでそれを呼び出す形でstateを変更する処理を実装します。
そのためにrenderSquare()を以下のように変更します。

renderSquare(i) {
  return (
    <Square value={this.state.squares[i]} onClick{() => this.handleClick(i)} />
  );
}

そして、Squareコンポーネントを以下のように書き換えます。

class Square extends React.Component {
  render() {
    return (
      <button> className="square" onClick={() => this.props.onClick()}
        {this.props.value}
      </button>
    );
  }
}

ここまで来たらひとまず現状を整理しましょう。
まず、BoardからSquareに渡されているpropsにはvalueとonClickなります。
valueはマスの値、onClickはこのあと定義するstateの状態を変更するhandleClick(i)メソッドとなります。
そして、Squareにおいてそれらをthis.props.value及びthis.props.onClick()として呼び出しています。
厳密には以下(チュートリアルから引用)のような処理になるようです。

  1. 組み込みのDOMコンポーネントである <button> に onClick プロパティが設定されているため React がクリックに対するイベントリスナを設定します。
  2. ボタンがクリックされると、React は Square の render() メソッド内に定義されている onClick のイベントハンドラをコールします。
  3. このイベントハンドラが this.props.onClick() をコールします。Square の onClick プロパティは Board から渡されているものです。
  4. Board は Square に onClick={() => this.handleClick(i)} を渡していたので、Square はクリックされたときに this.handleClick(i) を呼び出します。
  5. まだ handleClick() は定義していないので、コードがクラッシュします。Square をクリックすると、“this.handleClick is not a function” といった赤いエラー画面が表示されるはずです。

ちなみにReactではイベント(ここではクリックという行為)に対してon[Event](ex.onClick)、イベントを処理するメソッドには handle[Event](ex.handleClick) という名前を付けるのが慣習となっているようです。
では、未定義のhandleClickを定義していきましょう。

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
    };
  }

  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = 'X';
    this.setState({squares: squares});

  }

  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }

  render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}


handleClick()メソッド内にset.State()メソッドを入れることでstateの変更を行っています。
では、改めて現状を整理すると

  • SquareコンポーネントはBoard コンポーネントから値を受け取って、クリックされた時はそのことをBoardコンポーネントに伝えるだけになった。
  • Boardのstateが変更されると、個々のSquareコンポーネントもそれに合わせて再度レンダーされる。
  • よって全てのマス目の状態はBoardコンポーネント内で保持され、SquareコンポーネントはBoardコンポーネントに制御された状態となった。

ということになります。

さて、ここで注目するべきはconst squares = this.state.squares.slice();という部分です。
slice()は配列のコピーを作成するメソッドですが、わざわざsquaresのコピーを作成するより、直接squaresを書き換えるような形でいいのでは? という疑問が残ると思います。
それに対する解答が次に見ていくイミュータビリティという考え方です。

イミュータビリティについて

イミュータビリティ(immutability; 不変性)ということで、データを変更する際に元データに対して、書き換えではなく置き換えを行うというアプローチをする考え方です。
チュートリアルでの例を見てみます。

// ミューテート(書き換え)でのアプローチ
var player = {score: 1, name: 'Jeff'};
player.score = 2;
// Now player is {score: 2, name: 'Jeff'}

// 置き換えでのアプローチ

var player = {score: 1, name: 'jeff'};

var newPlayer = Object.assign({}, player, {score: 2});

//  Now player is unchanged, but newPlayer isn`t {score: 1, name: 'Jeff'}, however {score: 2, name: 'Jeff'}

Object.assignオブジェクトのコピーを作るメソッドです。
第一引数に空のオブジェクト、第二引数にコピーするオブジェクトを指定します。
さらにここで第三引数で参照したいオブジェクト指定することで、コピーしたオブジェクトの参照元を変更できます。
つまり、var newPlayer = Object.assign({}, player, {score: 2});playerオブジェクトをコピーしてscoreプロパティは{score: 2}を参照してくださいという意味になるわけです。
このあたりはオブジェクト指向について少し触れたことがある人は理解しやすいと思います。

では、なぜこういった手法を取るのかというと公式のチュートリアルでは理由が3つ挙げられています。

  1. 複雑な機能が簡単に実装できる

これはこのあと実装するタイムトラベル、つまりは手順の巻き戻しの機能を実装として例が挙げられています。
手順を巻き戻すということは例えば、playerオブジェクトのscoreが変動するということになるのですが、ここで書き換えの手法を使ってしまうとscoreプロパティを変更するのにいくつかの行程を踏む可能性が出てきてしまいます。
上記の例だけでも、手順を戻したり進めたりするだけで其の度にplayer.scoreに値を代入し直さないといけなくなります。
対して、置き換えの手法取ると参照するオブジェクトをplayerとnewPlayerとで切り替えればいいだけなので簡単ですね。

  1. 変更の検出が容易

概ね1と同じような理由ですがチュートリアルからの引用を載せておきます。

ミュータブル (mutable) なオブジェクトは中身が直接書き換えられるため、変更があったかどうかの検出が困難です。ミュータブルなオブジェクト変更の検出のためには、以前のコピーと比較してオブジェクトツリーの全体を走査する必要があります。
イミュータブルなオブジェクトでの変更の検出はとても簡単です。参照しているイミュータブルなオブジェクトが前と別のものであれば、変更があったということです。

  1. React の再レンダータイミングの決定がしやすい

これも上記2つと同じような理由ですね。
Reactはフロントエンドの技術なので画面をどう描画するかということを処理する役割を持ちます。
なので、画面が切り替わったりあるいは画面遷移がなくてもその場で今回のように手順の変更等があり、画面に変更がある場合などはその都度処理をするのですがその際、stateのどの部分に変更があったのかということがわからないと、それを探すのに差分をすべて探してしまいます。そうなると、当然処理は重くなり再描画が遅くなってしまいます。
しかし、置き換えの手法を用いると参照オブジェクトの切り替えだけで事が済むのでReactはstate内のどの部分に変更があったのか検知をして、差分の比較を変更があったオブジェクトのみで行うことができます。
すると、Reactは画面のどの部分を再描画したらいいのか判断をし、変更の必要のない部分は再描画を行わないという処理を実行するので処理に負担をかけずに済むわけですね。

以上、3点については以下の記事が簡潔にわかりやすくまとまっています。

Reactにおけるstateのイミュータビリティ

関数コンポーネント

さて、Boardコンポーネントに盤面の管理を完全に移行したのでSquareコンポーネントはもうpropsを受け取ってそれに基づいて描画内容を返す役割しかありません。
なので、クラスのコンポーネントではなくより簡潔な関数のコンポーネントに書き直します。
関数コンポーネントはstateを持たず、render()メソッドのみを持つコンポーネントのことを指します。

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>{props.value}</button>
  )
}

このようになります。
クラスではなくなったのでthis.propspropsになりました。
ちなみにそれと合わせてonClick={() => this.props.onClick()}onClick={props.onClick}と書き換えることができるみたいなのでそれに合わせておきます。

手番の処理

先手と後手の概念を付与します。
まずは、Boardコンポーネントのコンストラクタの部分を変更します。

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
      xIsNext: true,
    };
  }
}

xIsNextプロパティを新たに追加しました。
これを元に真偽値を判断してtrueである場合Xをそうでない場合はOを描画するように処理を書いていきます。
stateの状態を変更するメソッドはhandleClick()メソッドでしたね。
ついでにrender()メソッド内も変更して誰の手番か表示するようにします。

handleClick(i) {
  const squares = this.state.squares.slice();
  // 真偽値判断
  squares[i] = this.state.xIsNext ? 'X':'O';
  // 呼び出されるたびにxIsNextの状態を反転させる
  this.setState({
    squares: squares,
    xIsNext: !this.state.xIsNext,
  });
}

render() {
  const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}

勝者の判定

最後に勝者の判定を実装します。
チュートリアルでは以下の関数をコピペしろという支持があるのでコピペします。

function calculateWinner(squares) {

  // squareコンポーネントに与える配列のリスト
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];

  // 9 つの square の配列が与えられると、この関数は勝者がいるか適切に確認し、'X' か 'O'、あるいは null を返す。
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}

例えばi = 0出会った場合はconst[a,b,c] = [0,1,2]となります。
つまりsquares[0,1,2]となるわけですね。
で、この[0,1,2]は盤面のマスの位置を示しています。
なのでここまで書けばもうお分かりかと思いますが、

if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c])squares(XかOどちらかのプレイヤー)のマス目が3連になっているかの判定ということになります。
const linesはつまり、縦横斜めで3連になる組み合わせを表していたということです。

次にこれを呼び出して勝利判定を行うようにします。
Boardコンポーネントのrender()部分を変更しましょう。

render() {
  const winner = calculateWinner(this.state.squares);
  let status;
   if (winner) {
      status = 'Winner: ' + winner;
    } else {
      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
    }
}

最後に、handleClickメソッドを書き換えてゲームの決着がすでについている場合もしくは、クリックされたマス目が埋まっている場合にreturnするような処理を実装します。

 handleClick(i) {
    const squares = this.state.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      squares: squares,
      xIsNext: !this.state.xIsNext,
    });
  }


これで三目並べ自体は完成となりました。

タイムトラベル機能の追加

最後に手番を巻き戻せるようにタイムトラベル機能を実装します。
要は過去の手番を保存しておく配列を用意すればいいわけですね。
手番の状態はsquaresの配列で表現されるということは先程確認しました。
チュートリアルから引用すると

history = [
  // Before first move
  {
    squares: [
      null, null, null,
      null, null, null,
      null, null, null,
    ]
  },
  // After first move
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, null,
    ]
  },
  // After second move
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, 'O',
    ]
  },
  // ...
]

このような感じで保存しておけるようにしたいということになりますね。
では進めていきます。

### stateのリフトアップ再び

上記を見るとわかると思いますが、historyは手番をすべて管理している状態と言えます。
そして、historyは状態が常に変化していくこともここまでくればわかると思います。
つまり、historyはstateで管理され、そうなると盤面のマスの状態はhistoryから引っ張ってくればいい=propsとしてやり取りするような処理を書けるということになりますね。
なので、Boardよりさらに上位のGameコンポーネントを作り、そこにstateをリフトアップしていくというのが今回やることです。

では、作業していきましょう。
まずはコンストラクタの移行ですね。

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(9).fill(null),
      }],
      xIsNext: true,
    };
  }

  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}


次にGameコンポーネントのrender()をhistoryが使われるように変更します。
Boardコンポーネントからrender()のうちゲームテキスト表示の処理をこちらに移植します。
また、それに伴いhandleClick()も同時に移植してしまいます。

  handleClick(i) {
    const history = this.state.history;
    const current = history[history.length - 1];
    const squares = this.current.squares.slice();
    if(calculateWinner(squares) || squares[i]) {
      return;
    }

    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares,
      }]),
      xIsNext: !this.state.xIsNext,
    });
  }

  render() {
    // historyを引っ張ってくる
    const history = this.state.history;
    // historyに保存されているsquares配列より最新のものを取得
    const current = history[history.length - 1];
    const winner = calculateWinner(current.squares);
    let status;
    if (winner) {
      status = 'Winner: ' + winner;
    } else {
      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
    }

    return (
      <div className="game">
        <div className="game-board">
          <!-- ゲームのステータステキストを表示 -->
          <Board
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }



最後にリフトアップに合わせてBoardコンポーネントをpropsを受け取る形に書き換えます。
やることは

  • コンストラクタの削除
  • this.state.squaresthis.props.squaresへと置き換え
  • this.handleClickthis.props.onClickへと置き換え
  • render()のうち移植した部分を削除

  • handleClick()の削除

こう見るとリフトアップのポイントはどのプロパティをリフトアップするのか(=propsとして受け取ることになるのか)ということを理解することなのかもしれませんね。

class Board extends React.Component {

  renderSquare(i) {
    return(
      <Square value={this.props.squares[i]} onClick{() => this.props.onClick(i)} />
    );
  }

  render() {
    return (
      <div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

過去の着手の表示

過去の手番に「ジャンプ」するためのボタンの一覧を作成していきます。
Gameコンポーネントのrender()に処理を追加をしていきます。

render() {
  const history = this.state.history;
  const current = history[history.length - 1];
  const winner = calculateWinner(current.squares);

  const moves = history.map((step, move) => {
    const desc = move ? 'Go to move #' + move :'Go to game start';

    return(
      <li>
        <button onClick={() => this.jumpTo(move)}>{desc}</button>
      </li>
    );
  });

    return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <!-- 手番ボタンの表示 -->
          <ol>{moves}</ol>
        </div>
      </div>
    );

}

map()はある配列を別の配列に作り直すことができます。
チュートリアルでは以下のような例で説明されています。

const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]

historyには手番がsquaresの配列の形ですべて保存されているわけですから、手番を戻す際にhistoryから該当する手番の配列を取得して返したいということになるわけです。
故に上記のようなコードになったわけですが、このままではjumpTo()が未定義であることと、keyの概念が欠如しているのでエラーが出ます。
Keyについてはプロパティにおけるキー、またはデータベースにおけるIDなどと同じような考え方で、Reactではリストのレンダーに関してその履歴に対し個々にユニークIDを持っているわけです。
これはどういうことかというと例えば今回は手番が進むたびに画面が更新されるわけですが、その個々の描画がそれぞれIDを持つということになります。
故にこのIDを用いて、履歴のインデックスとすることが可能というわけですね。
ちなみにKeyはこちらから参照することはできず、Reactから自動的に使用されるようです。

閑話休題、よってjumpTo()の実装とKeyの明示を行わなければいけません。
以下の通りに勧めていきます。

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(9).fill(null),
      }],
      // 何手目であるかを表すプロパティを追加
      stepNumber: 0,
      xIsNext: true,
    };
  }

  handleClick(i) {
    // ある手番(A)から過去手番(B)に戻り、そこから新しい手を打った場合、これまでのAまでの手番を参照せずにそれらの履歴を削除する
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    const current = history[history.length - 1];
    const squares = current.squares.slice();
    if(calculateWinner(squares) || squares[i]) {
      return;
    }

    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares
      }]),
      // 手番の巻き戻しを行い、そこから新しい手を打った場合の手番の更新
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext.
    });
  }


  jumpTo(step) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    });
  }

  render() {
    // this method has not changed
    const moves = history.map((step, move) => {
      const desc = move ?
        'Go to move #' + move :
        'Go to game start';
      return (
        <li key={move}>
          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      );
    });

     return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <!-- 手番ボタンの表示 -->
          <ol>{moves}</ol>
        </div>
      </div>
    );
  }
}


まず、render()の描画の部分に<li key={move}>という形でKeyを明示してやります。
また、何手目の状態であるかを管理するためのstepNumberプロパティを追加しています。
それに伴い、render()current変数を追加し、historyからこのプロパティを用いて現在選択されている手番を取得するように変更します。
次に、そのstepNumberが更新されるように。jumpTo()を定義していきます。
ちなみに先程からいきなりstep及びmoveが引数として登場していますが、コードを見る限りではstepは手番、moveはsquaresの配列を表しているみたいです。
従って、jumpTo()は手番を更新し、かつそれに伴いxIsNextつまりはプレイヤーがどちらであるか設定しているということになります。

ここまで来たら最後にhandleClick()を更新します。
主に巻き戻しを行うのに備えての指定の仕方の変更ですね。
上記のコードにコメントしてありますが、例えばsetState()の部分だとstepNumberのプロパティをstepNumber: history.lengthとしています。
これは先程のjumpTo()が単純にstepを手番として管理していたのに対し、巻き戻しを行った際はそれでは管理できないのでhistory.lengthから引っ張ってくるということになるわけです。

これで、React公式チュートリアルは完了です。
お疲れ様でした、全体のコードは以下のようになっていると思います。

最終的なコード

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

class Board extends React.Component {
  renderSquare(i) {
    return (
      <Square
        value={this.props.squares[i]}
        onClick={() => this.props.onClick(i)}
      />
    );
  }

  render() {
    return (
      <div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [
        {
          squares: Array(9).fill(null)
        }
      ],
      stepNumber: 0,
      xIsNext: true
    };
  }

  handleClick(i) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    const current = history[history.length - 1];
    const squares = current.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? "X" : "O";
    this.setState({
      history: history.concat([
        {
          squares: squares
        }
      ]),
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext
    });
  }

  jumpTo(step) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0
    });
  }

  render() {
    const history = this.state.history;
    const current = history[this.state.stepNumber];
    const winner = calculateWinner(current.squares);

    const moves = history.map((step, move) => {
      const desc = move ?
        'Go to move #' + move :
        'Go to game start';
      return (
        <li key={move}>
          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      );
    });

    let status;
    if (winner) {
      status = "Winner: " + winner;
    } else {
      status = "Next player: " + (this.state.xIsNext ? "X" : "O");
    }

    return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={i => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

ReactDOM.render(<Game />, document.getElementById("root"));

function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}



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

Next.js公式examples集を分類

はじめに

Next.jsの公式サンプルは現在236本あります。(2020/07/22)
しかし、このままではどれを見たらいいのかわからないので主観で分類してみました。

Next.js公式examples集

Next.jsの基礎1易しい
basic-css (基礎)CSSinJSなどを使っている
basic-export (基礎)最も基本的な使用法
dynamic-routing (基礎)動的ルーティングの例
environment-variables (基礎)環境変数の使い方
head-elements (基礎)ヘッド要素の例
hello-world (基礎)Next.jsの基本中の基本
layout-component (基礎)一般的な使用例
nested-components (基礎)コンポーネントをネストして利用する例
using-router (基礎)コンポーネントの代わりにnext/routerを使ってページをリンクする
with-app-layout (基礎)_app.jsを使用してすべてのページにグローバルレイアウトを実装する
with-loading (基礎)ページ切替時に時間がかかる時、Loading画面を表示する技術
Next.jsの基礎2普通
api-routes (公式)基本的なAPIルートの例
api-routes-middleware (公式)APIルート
api-routes-rest (公式)独自のルートを構築する簡単なソリューションを提供するAPIルートが付属していますAPI。この例は、それを使用してREST APIを作成する方法を示しています。
catch-all-routes ページをデータから自動的に作成してくれる機能
data-fetch サーバーとクライアント間でのデータ取得
gh-pages Nextの背後にある最も基本的なアイデアを示しています。
with-context-api アプリでreact context apiを使用する方法
with-dynamic-app-layout app.jsを使用してページの_dynamicレイアウトを実装する方法
with-dynamic-import モジュールを動的にインポートする方法
with-env-from-next-config-js next.config.jsを使って環境変数をビルド時に設定します
with-next-page-transitions ページ間移動時において読み込み状態を簡単に追加
with-prefetching ページをプリフェッチ(事前読み込み)するサンプルアプリ
with-shallow-routing (公式)ShallowRoutingExample
with-static-export 静的エクスポートの例
with-typescript タイプスクリプトを使って型を適用
with-typescript-eslint-jest NextJSTypescriptボイラープレート
with-typescript-types TypeScript型システムをNext.jsに統合する方法を示しています。
UI関連ant
with-ant-design AntDesignofReactとともにNext.jsを使用する方法
with-ant-design-less AntDesignofReactとともにNext.jsを使用する方法
with-ant-design-mobile antd-mobileとともにNext.jsを使用する方法
with-ant-design-pro-layout-less AntDesignProLayoutofReact.とともにNext.jsを使用する方法
UI関連その他
with-atlaskit AtlaskitofReactとともにNext.jsを使用する方法AltassianのオフィシャルUIライブラリ
with-chakra-ui Webサイトやアプリの作成を非常に簡単にする、アクセス可能で再利用可能で構成可能なReactコンポーネントのセットchakra-uiをNext.jsアプリ内のコンポーネントライブラリとして使用する方法
with-grommet GrommetUIlibrary
with-patternfly RedHatが作っているUIフレームワークUIパターン集
with-rebass UIコンポーネントRebassはReactのブートストラップです。
Firebase関連
with-firebase クライアントサイドアプリケーション用のFirebaseの簡単な設定です。
with-firebase-authentication サーバーレスAPIを使用したFirebase認証の例
with-firebase-cloud-messaging FirebaseCloudMessagingの例
with-firebase-hosting Firebaseホスティングの例
GraphQL関連
api-routes-graphql GraphQLServerapollo-server-micro
with-graphql-faunadb FaunaDBGraphqlスターターの例-FaunaDBゲストブック
with-graphql-hooks GraphQLフックの例GraphQLHooksは、最小限のフック優先のGraphQLクライアントになることを目的としたNearFormのライブラリです。
with-graphql-react graphql-react最新のコンテキストを使用したReactのGraphQLクライアント
with-react-relay-network-modern Theproduction-readyGraphQLclientforReact.
with-reason-relay GraphQLbackend
with-relay-modern Theproduction-readyGraphQLclientforReact.
with-typescript-graphql GraphQLとtypescriptの使用例
with-urql 高度にカスタマイズ可能で用途の広いGraphQLクライアント
apollo関連
api-routes-apollo-server ApolloはGraphQLクライアント
api-routes-apollo-server-and-client ApolloはGraphQLクライアント
api-routes-apollo-server-and-client-auth ApolloはGraphQLクライアント
with-apollo ApolloはGraphQLクライアント
with-apollo-and-redux ApolloはGraphQLクライアント
コンポーネント関連
with-carbon-components IBM'scarbon-components-react
server関連
custom-server (公式)
custom-server-actionhero ActionHero
custom-server-express ExpressWebアプリケーション
custom-server-fastify ○Fastify
custom-server-hapi SecureFramework
custom-server-koa newwebframeworkdesigned
custom-server-polka Express.jsの代替品
custom-server-typescript Nodemonを使用してサーバーとクライアントの両方でTypeScriptを使用し、Next.jsユニバーサルコードに影響を与えずにサーバーコードをライブでリロードする方法
storybook関連
with-storybook UIコンポーネントを開発するためのオープンソースツール
with-storybook-typescript UIコンポーネントを開発するためのオープンソースツール
検索関連
with-algolia-react-instantsearch 検索の実装と検索の分析
監視関連
with-sentry アプリケーション監視プラットフォーム
with-sentry-simple アプリケーション監視プラットフォーム
リッチテキスト関連
with-draft-js RichTextEditorFrameworkforReact
with-quill-js powerfulrichtexteditor
with-slate リッチテキストThisiseditablerichtext
DB関連
with-mongodb-mongoose MongoDB
with-prisma databasetoolkit
with-realm-web MongoDBRealmWebSDK
認証関連
auth0 (認証)認証サポートを簡単に追加する方法
with-cookie-auth-fauna (認証)ユーザーを認証し、トークンを安全な(JS以外の)Cookieに格納します。
with-iron-session (認証)署名および暗号化されたCookieを使用してセッションデータを格納する認証システムを作成
with-magic (認証)Magic電子メールベースのマジックリンクを使用した、Cookieベースのパスワードなし認証を特徴としています。
with-next-auth (認証)オープンソースで使いやすく、デフォルトで安全な認証ライブラリを備えたnext-authを使用
with-passport (認証)ユーザー名とパスワードを使用したCookieベースの認証
with-passport-and-next-connect Passport.jsでnext-connectおよびcookieベースの認証を使用して、基本的なCRUDアプリを作成します。
with-userbase Userbaseユーザーアカウントとデータの永続性を静的サイトに追加する最も簡単な方法です
CSS関連
with-aphrodite CSSinJS
with-astroturf ZeroruntimeCSSinJS
with-cxs minimalCSSinJS
with-emotion CSSinJSSSR中にサーバーデータを直接Next.jsページに渡す
with-emotion-11 CSSinJS
with-fela CSS関連
with-filbert CSSinJSソリューション(フレームワーク)filbert-jsでNext.jsを使用する
with-glamor CSS関連スタイリングソリューションの使用方法
with-glamorous (廃止)代替はemotionを推奨
with-goober 小さなcss-in-js
with-linaria CSSstylingsolutioninsteadofstyled-jsx
with-next-css CSSモジュールサポートの使用方法
with-next-less CSSLess
with-next-sass Sass/Scssモジュールサポートの使用方法
with-rbx-bulma-pro BulmaissimplyacollectionofCSSclasses.
with-react-bootstrap react-bootstrapreact用に再構築されたbootstrap
with-react-jss JSSはCSSのオーサリングツール
with-react-md (紹介ページが消えている、V2になってページが移動した?)SCSSreact-md(ReactMaterialDesign)
with-react-with-styles CSSinJS
with-semantic-ui CSSフレームワークjQueryで実装されている
with-stitches CSSinJS
with-stitches-styled CSSinJS
with-style-sheet CSSinJSlibrarystyle-sheet
with-styled-components CSSstyled-components
with-styled-jsx-plugins CSSCSSPreprocessingviaPlugins
with-styled-jsx-scss PostCSS、SASS(SCSS)、LESS、またはstyled-jsxを備えたその他のプリプロセッサを使用できます。
with-styletron CSSinJSスタイルソリューション
with-tailwindcss tailwindCSS
with-tailwindcss-emotion tailwindCSS
with-typescript-styled-components StyledComponentsとTypeScriptの使用例CSSinJSの中で最も人気のあるライブラリ
with-typestyle CSSスタイルソリューションを使用する方法
状態管理redux関連
with-redux Redux関連
with-redux-code-splitting Redux関連
with-redux-observable Redux関連
with-redux-persist Redux関連
with-redux-saga Redux関連
with-redux-thunk Redux関連
with-redux-toolkit Redux関連
with-redux-wrapper Redux関連
with-rematch Redux関連
状態管理その他
with-kea 状態管理フレームワーク
with-mobx 状態管理アプリのグローバルな状態を取得したい場合
with-mobx-react-lite 状態管理
with-mobx-state-tree 状態管理
with-mobx-state-tree-typescript 状態管理
with-overmind 状態管理
with-recoil 状態管理ライブラリ
with-unstated unstated-nextReact状態管理ライブラリ
amp関連
amp Next.jsとAMP機能を使用してAMPページを作成する方法
amp-first AMPファーストサイトのボイラープレートを設定
amp-story Next.jsとAMP機能を使用してamp-storyを含むAMPページを作成する方法
国際化
with-i18n-rosetta 汎用国際化ライブラリ
with-lingui LinguiJS-JavaScriptでのシームレスな国際化
with-next-i18next i18nextに基づく国際化をnext.jsアプリケーションに追加する最も簡単な方法
with-next-translate next-translate Next.jsページを翻訳するためのツールです。
with-react-intl FormatJSクライアントとサーバーでWebアプリを国際化します。
AWS関連
with-aws-amplify AWSAmplifyは、モバイルおよびフロントエンドのウェブデベロッパーがAWSを利用して安全でスケーラブルなフルスタックアプリケーションを構築できるようにするツールとサービスのセットです。
with-aws-amplify-typescript AWSAmplifyは、モバイルおよびフロントエンドのウェブデベロッパーがAWSを利用して安全でスケーラブルなフルスタックアプリケーションを構築できるようにするツールとサービスのセットです。
テスト関連
with-jest テスト
with-mocha テスト
with-tesfy A/Bテストと機能フラグ
webpack関連
with-webpack-bundle-size-analyzer Webpackバンドルのサイズに寄与しているものを見つけるのに役立つ小さなユーティリティ。
cms関連
blog-starter Next.jsとMarkdownを使用して静的に生成されたブログの例
blog-starter-typescript Next.js、Markdown、TypeScriptを使用して静的に生成されたブログの例
cms-agilitycms AgilityCMS
cms-buttercms ButterCMS
cms-contentful Contentful
cms-cosmic Cosmic
cms-datocms DatoCMS
cms-graphcms GraphCMS
cms-prismic Prismic
cms-sanity Sanity
cms-storyblok Storyblok
cms-strapi Strapi
cms-takeshape TakeShape
cms-wordpress WordPress
with-netlify-cms Gitワークフロー用のオープンソースのコンテンツ管理システム
デスクトップアプリケーション関連
with-electron デスクトップアプリケーションElectronapplicationexample
with-electron-typescript デスクトップアプリケーションElectronapplicationexamplewithtypescript
google関連
with-google-analytics GoogleアナリティクスとともにNext.jsを使用する方法
with-google-analytics-amp AMPと組み合わせてGoogleアナリティクスとともにNext.jsを使用する方法
with-react-ga react-gaReactGoogleAnalyticsModule
実験中
z-experimental-refresh (実験的)アプリケーションやプロジェクトでは、これらの機能を(まだ)使用しないでください。
古い、数年更新がない
with-react-toolbox 古い怪しいリンクに飛ぶ
with-reflux 古いstoreAsimplelibraryforunidirectionaldataflowarchitectureinspiredbyReactJSFlux.
moved、removed、非推奨、廃止
page-transitions (removed)/examples/with-next-page-transitions
parameterized-routing (removed)examples/dynamic-routing
with-cookie-auth (非推奨)(認証)
with-dotenv (インストール不要)Next.js9.4からサポートされています。
with-expo ExpoユニバーサルReactアプリケーションのプラットフォームNext.jsのサポートは試験的なものです。
with-expo-typescript ExpoユニバーサルReactアプリケーションのプラットフォームNext.jsのサポートは試験的なものです。
with-firebase-authentication-serverless (moved)with-firebase-authentication
with-flow Flow静的型チェッカーFlowを使用したアプリの例
with-framer-motion プロダクション対応のアニメーションライブラリです。
with-global-stylesheet (removed)こちらを利用してくださいexamples/with-next-css
with-global-stylesheet-simple (removed)こちらを利用してくださいexamples/with-next-css
with-markdown (moved)next.js/examples/blog-starter/
with-material-ui (moved)mui-org/material-ui
with-next-routes (インストール不要)Next.jsでサポートされています。
with-now-env (インストール不要)Next.js9.4からサポートされています。
with-pretty-url-routing (廃止)Next.jsではデフォルトで動的ルーティングがサポートされています。
with-strict-csp-hash (moved) with-strict-csp
with-styled-jsx-postcss (moved) with-styled-jsx-plugins
with-sw-precache (非推奨)代わりにusingcanary/examples/with-next-offline
with-universal-configuration-build-time (removed)Next.js9.4からサポートされています。
with-universal-configuration-runtime (インストール不要)Next.js9.4からサポートされています。
with-webpack-bundle-analyzer (moved)analyze-bundles
その他
active-class-name Linkコンポーネントにプロパティをつけてリンク動的に変更
analyze-bundles @next/bundle-analyzerを使用して出力バンドルを分析する方法
api-routes-cors Cross-OriginResourceSharing(CORS)追加のHTTPヘッダーを使用して、ある起点で実行されているWebアプリケーションに、異なる起点から選択されたリソースへのアクセスを許可するようにブラウザーに指示するメカニズムです。
custom-routes-proxying カスタムルートプロキシの例
progressive-render プログレッシブサーバー側レンダリングを実装するアプリの例
ssr-caching SSR化されたページをメモリにキャッシュするアプリの例
svg-components svgファイルのインポートおよびReactコンポーネントとしてのレンダリングのサポートを追加します。
using-preact Reactの代わりにPreactを使用
with-absolute-imports Webpackの構成を変更せずに、相対インポートではなく絶対インポートを持つようにBabelを構成する方法
with-babel-macros babel-macrosを使用したアプリの例単純なコンパイル時ライブラリを構築できます???
with-cerebral CerebralJSによる宣言的な状態と副作用の管理
with-custom-babel-config カスタムBabel構成の使用
with-custom-reverse-proxy リバースプロキシの例
with-docker 実行時にdockerアプリケーションのカスタム環境変数を設定する方法
with-dynamic-app-layout app.jsを使用してページの_dynamicレイアウトを実装する方法
with-dynamic-import モジュールを動的にインポートする方法
with-hls-js HTTPライブストリーミングクライアントを実装するJavaScriptライブラリ
with-http2 HTTP2サーバーを使用する最も基本的な例
with-mdx MDXは Markdownに直接 JSX を挿入できるフォーマット
with-monaco-editor VSCodeを強化するコードエディター
with-mux-video ビデオ用のAPI
with-next-offline next-offline pluginの使い方
with-next-offline オフライン機能を簡単に有効化
with-next-seo next-seoSEOの管理を簡単にするプラグインです。
with-orbit-components Kiwi.comの製品を構築する最も簡単な方法を開発者に提供するReactコンポーネントライブラリです。
with-polyfills ポリフィルとは、最近の機能をサポートしていない古いブラウザーで、その機能を使えるようにするためのコードです。
with-react-helmet react-helmetHeadタグに関する全てを管理
with-react-multi-carousel react-multi-carouselは、外部依存関係のない複数のアイテムをサポートするサーバー側でレンダリングするカルーセルを提供するReactコンポーネントです。
with-react-native-web ReactNativeforWeb
with-reasonml OCamlツールチェーンReasonを使用すると、JavaScriptとOCamlの両方のエコシステムを活用しながら、シンプルで高速かつ高品質なタイプセーフコードを記述できます。
with-reasonml-todo OCaml
with-route-as-modal モーダルウィンドウ?を条件付きで表示する方法
with-segment-analytics セグメント分析
with-stencil Webコンポーネントおよび高性能Webアプリ用のコンパイラ
with-stomp テキスト指向メッセージングプロトコル
with-strict-csp ContentSecurityPolicy(CSP)厳密なCSP生成スクリプトハッシュを使用したアプリの例
with-stripe-typescript stripeオンライン決済
with-styled-components-rtl 右から左にすると、サイト内のすべての要素を「反転」して、右から左に読む文化(たとえば、アラビア語)のニーズに合わせることができます。
with-three-js WebGLレンダラーを備えた軽量の3Dライブラリ
with-videojs opensourceHTML5playerframework
with-web-worker worker.jsWebpack内でWebワーカーを自動的にバンドルしてコンパイルします。
with-webassembly WebAssemblyRust
with-why-did-you-render 特定のコンポーネントがいつ、なぜ再レンダリングされるかを簡単に追跡するのにも役立ちます。
with-xstate ステートマシン
with-yarn-workspaces デフォルトで利用可能なパッケージアーキテクチャをセットアップする新しい方法
with-zeit-fetch (Vercel本家)@zeit/fetchの使い方
with-zones 複数のアプリを一つのURLで
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む