- 投稿日:2020-07-22T21:34:33+09:00
初めての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 API
とDirections API
)を有効化Mapの描画
基本となるGoogleMapを描画するMapコンポーネントを作ります。
Snazzy Mapsからカスタムデザインをインポートすることができます。
GoogleMapComponent.jsimport 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コンポーネントを作ります。
GoogleMapComponent.jsimport PlaceInfo from "./PlaceInfo"; export default function GoogleMapComponent() { return ( <GoogleMap> <PlaceInfo/> </GoogleMap> ); }PlaceInfo.jsimport 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} </> ); }マーカーアイコンのクラスター表示
マーカーアイコンをいくつも表示するとアイコン同士が重なって見えにくくなってしまいます。
それを回避するために、地図の表示倍率にしたがってクラスター表示することができます。
今回はクラスター表示にカスタムアイコンを使用する方法も併せて紹介します。
GoogleMapComponent.jsimport CustomMarkerClusterer from "./CustomMarkerClusterer"; export default function GoogleMapComponent() { return ( <GoogleMap> <CustomMarkerClusterer/> </GoogleMap> ); }CustomMarkerClusterer.jsimport 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地点を結ぶ最短ルートだけでなく、多地点を結ぶ最短ルートを表示することができます。
今回は、始点と終点と経由地を指定することで、経由地の順序を最適化してルートを表示する方法を紹介します。
GoogleMapComponent.jsimport Direction from "./Direction"; export default function GoogleMapComponent() { return ( <GoogleMap> <Direction/> </GoogleMap> ); }Direction.jsimport 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を実装したい方にはマストなライブラリだと思いました。参考サイト
- 投稿日:2020-07-22T16:58:59+09:00
Reactの紹介
はじめに
仕事や趣味の開発でReactを使用してみたので簡単に紹介してみようと思います。
Reactとは
- ReactはWebアプリケーションのUIを作るためのライブラリ(※フレームワークではない)
- Facebookがオープンソースとして公開している
- FacebookやNetflixやAtlassianなど色々なサイトが利用している
Reactを使うメリット
パフォーマンスが良い
DOMとは
DOMはページを表示する際に作られるもの、ブラウザはこれをもとにページを描画している
※jQueryはこのDOMを操作している既存の仕組みが遅い理由
既存の仕組みだと受け取った情報を元に毎回DOMを1から構築し、ブラウザに描画しているため遅い
Reactの仕組み
Reactでは先に仮想DOMを作成し、仮想DOMからDOMを作成する仕組みになっている
※最初に仮想DOMを作っているのが大事
仮想DOMとは
実際のDOMと対になるもので、DOMよりはやく作ることができるように設計されたもの
※HTMLを構築する木構造データでJavascript内でDOMを仮想的に作っているらしいです
画面が更新されるまでの流れ
Reactでは画面に更新があった際、1つ前の仮想DOMと差分を確認し、差分だけDOMに反映させる
jQueryより見やすい
人によるかもだけど項目の出し分けなどはjQueryよりReactの方が見やすいと思います
例えば以下はボタンを押すとpタグが表示されるコードをjQueryで書いた例になります。
これをReactで書くと以下のようになると思います。これはクラスで書いてありますが、関数で書くともう少しシンプルになると思います。
プロジェクトを作るのが簡単
プロジェクトの作成は「npm」で提供されている「create-react-app」コマンドを使えるようにしておけば、1コマンドで作れる。
このコマンドが出るまでは自分でディレクトリを作る必要があった模様。
プロジェクトにNodeJSのサーバーが含めれているので、デバッグが楽
ReactのプロジェクトにはNodeJSサーバーが入っているので「npm start」とコマンドを叩くだけで動作確認をすることができる。
NodeJSサーバーが立ち上がっている状態で編集を加えると自動でコンパイルされ画面に反映される。
記法が間違っている場合はコンパイル時にエラーとなる
NodeJSサーバーが立ち上がっている状態で間違った記法で書いて保存するとエラーが表示される
書き方がパーツ単位のため再利用しやすい
Reactでは画面のパーツ(一覧とか)を別のファイルに定義することができるのでヘッダーなど画面ごとに定義する必要がなく再利用がしやすい。
ビルドするとNodeJSが入っていない環境でも動く
ビルドすると動かすのに必要なJSファイル等が生成されるため、NodeJSが入っていない環境でも動く
AndoridアプリやIOSアプリも作ることができる(ReactNative)
別途ライブラリ(react-native)を入れる必要があるが、Reactの記述方法でアプリを作ることもできる
アプリを作る際、動作確認をする度に毎回コンパイル等の作業が発生するがReactNativeの場合は保存したタイミングでデバック用のスマホに反映されるので楽
Reactを使うデメリット
デプロイする際はapache等のドキュメントルートをプロジェクトのエンドポイントに指定する必要がある
Reactはビルドを行うとドキュメントルートに配置する前提でパスを生成するため、サブディレクトリ等に配置するとJSやCSSなどが404となってしまうので注意!
※サブディレクトリに配置する場合は別途ライブラリが必要MVC的な書き方はできない
Reactはあくまで画面を作るライブラリなのでMVC的な書き方をすることができない
※データを取りに行く場合はAPIから取得するような形になる
JSXがわかりにくい
JSXとはReactでHTML(DOM)を出力するための独自構文のこと
他の言語と違ってhtmlとかがそのまま代入できたりするので気持ち悪いと思う人もいる模様
まとめ
- 簡単な静的ページ(htmlが1枚のページとか)を作るならjQuery、動的なページを作るならReactの方が向いてそう
- MVC形式で作るならReactは向いていない(フレームワークによっては埋め込めるものあるらしい)
- 高速なページを作るなら仮想DOMが使われているReactの方が良さそう
- 投稿日:2020-07-22T16:22:02+09:00
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'] } } }, }
- 投稿日:2020-07-22T16:22:02+09:00
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'] } } }, }
- 投稿日:2020-07-22T13:50:55+09:00
React で Markdown を HTML に変換しつつ簡単に syntax highlight したい
使うもの
- react-markdown
- Markdown を HTML に変換してくれるライブラリ
- React Syntax Highlighter
- 受け取った文字列を syntax highlight してくれるライブラリ
実装例
~/components/CodeBlock.tsximport 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 してしまうとエラーとなるので、環境に合わせてesm
かcjs
か選択する。
他に使えるオプションとかはそれぞれの README に書いてた。
ブログに書いたものコピペです
- 投稿日:2020-07-22T02:16:42+09:00
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とはclass
がclassName
になっているところや<ShoppingList />
と書けばそれ全体を指定することもできます。
例えば<div />
としてやれば、divタグすべてが指定されます。Propsについて
では公式チュートリアルのように三目並べを作っていきます。
この三目並べは
- Square
- Board
- Game
の3つのコンポーネントから構成されます。
実はこの3つのコンポーネントは親子関係ができていたりするのですが、それは少しずつ見ていくとしてまずは盤面を作ってみます。
そのためのコードが以下の部分です。src/index.jsimport 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()
として呼び出しています。
厳密には以下(チュートリアルから引用)のような処理になるようです。
- 組み込みのDOMコンポーネントである
<button>
に onClick プロパティが設定されているため React がクリックに対するイベントリスナを設定します。- ボタンがクリックされると、React は Square の render() メソッド内に定義されている onClick のイベントハンドラをコールします。
- このイベントハンドラが this.props.onClick() をコールします。Square の onClick プロパティは Board から渡されているものです。
- Board は Square に onClick={() => this.handleClick(i)} を渡していたので、Square はクリックされたときに this.handleClick(i) を呼び出します。
- まだ 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つ挙げられています。
- 複雑な機能が簡単に実装できる
これはこのあと実装するタイムトラベル、つまりは手順の巻き戻しの機能を実装として例が挙げられています。
手順を巻き戻すということは例えば、playerオブジェクトのscoreが変動するということになるのですが、ここで書き換えの手法を使ってしまうとscoreプロパティを変更するのにいくつかの行程を踏む可能性が出てきてしまいます。
上記の例だけでも、手順を戻したり進めたりするだけで其の度にplayer.score
に値を代入し直さないといけなくなります。
対して、置き換えの手法取ると参照するオブジェクトをplayerとnewPlayerとで切り替えればいいだけなので簡単ですね。
- 変更の検出が容易
概ね1と同じような理由ですがチュートリアルからの引用を載せておきます。
ミュータブル (mutable) なオブジェクトは中身が直接書き換えられるため、変更があったかどうかの検出が困難です。ミュータブルなオブジェクト変更の検出のためには、以前のコピーと比較してオブジェクトツリーの全体を走査する必要があります。
イミュータブルなオブジェクトでの変更の検出はとても簡単です。参照しているイミュータブルなオブジェクトが前と別のものであれば、変更があったということです。
- React の再レンダータイミングの決定がしやすい
これも上記2つと同じような理由ですね。
Reactはフロントエンドの技術なので画面をどう描画するかということを処理する役割を持ちます。
なので、画面が切り替わったりあるいは画面遷移がなくてもその場で今回のように手順の変更等があり、画面に変更がある場合などはその都度処理をするのですがその際、stateのどの部分に変更があったのかということがわからないと、それを探すのに差分をすべて探してしまいます。そうなると、当然処理は重くなり再描画が遅くなってしまいます。
しかし、置き換えの手法を用いると参照オブジェクトの切り替えだけで事が済むのでReactはstate内のどの部分に変更があったのか検知をして、差分の比較を変更があったオブジェクトのみで行うことができます。
すると、Reactは画面のどの部分を再描画したらいいのか判断をし、変更の必要のない部分は再描画を行わないという処理を実行するので処理に負担をかけずに済むわけですね。以上、3点については以下の記事が簡潔にわかりやすくまとまっています。
関数コンポーネント
さて、Boardコンポーネントに盤面の管理を完全に移行したのでSquareコンポーネントはもうpropsを受け取ってそれに基づいて描画内容を返す役割しかありません。
なので、クラスのコンポーネントではなくより簡潔な関数のコンポーネントに書き直します。
関数コンポーネントはstateを持たず、render()
メソッドのみを持つコンポーネントのことを指します。function Square(props) { return ( <button className="square" onClick={props.onClick}>{props.value}</button> ) }このようになります。
クラスではなくなったのでthis.props
はprops
になりました。
ちなみにそれと合わせて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.squares
をthis.props.squares
へと置き換えthis.handleClick
をthis.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; }
- 投稿日:2020-07-22T01:35:30+09:00
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で