- 投稿日:2020-09-24T18:38:30+09:00
Next.jsを ver8.1.0からver10.0.3に上げたログ
この記事はLivesense アドベントカレンダー2020の20日目の記事です。
今期、プロダクトで利用しているNext.jsのバージョンをv8.1.0からv10.0.3まで上げたので、マイナーバージョンごとのアップグレード時の作業内容を公開しようと思います。
今後同じような作業をする方の参考になると嬉しいです。転職会議チームはページが取り扱うドメインごとにリポジトリが分かれていて、2020年12月現在Next.jsを利用しているページの例が以下です。
企業検索ページ: https://jobtalk.jp/companies/search
企業詳細ページ: https://jobtalk.jp/companies/4075, https://jobtalk.jp/companies/4075/answers などこれらのページのフロントエンドアプリケーションがNext.jsで作成されていて、データ取得時にRails製のBFFおよびAPIサーバーと通信する、というような構成です。利用している主なライブラリのバージョンは以下の通りです。
"react": "^16.13.1", "redux": "^4.0.5", "redux-observable": "^1.2.0", "typescript": "^3.8.3",アップグレードは以下のような方針で進めました。
- マイナーバージョンごとのアップグレードを行い都度打鍵
- マイナーバージョンアップグレード時のパッチバージョンはその時点で最新のものを適用
- 大幅な修正が入る対応などについてはマイナーバージョンアップグレードとは分けて対応
8.1.0 -> 9.0.8
Next.jsおよび関連ライブラリのバージョンアップグレード
package.json{ ... "dependencies": { ... - "next": "^8.1.0", + "next": "9.0.8", - "next-redux-wrapper": "^3.0.0", + "next-redux-wrapper": "^4.0.1", // Next.js ver9への対応 - "@zeit/next-typescript": "^1.1.1", }, "devDependencies": { - "@types/next": "^8.0.5", // ビルトインTSサポートにより不要化 - "@types/next-redux-wrapper": "^2.0.2", // ビルトインTSサポートにより不要化 - "fork-ts-checker-webpack-plugin": "^4.1.3", // ビルトインTSサポートにより不要化 }, ... }NextFC廃止 => NextPageへ
src/pages/companyDetail.tsximport { NextPage } from 'next' // 型をちゃんとしてないのは一旦後回し - const CompanyPage: NextFC<PropsType, { companyId: number; isServer?: boolean }, any> = ({ + const CompanyPage: NextPage<any> = ({ ...next/linkの外部リンクとしての使用法の廃止
ver8系以前はリポジトリ外へのリンクに
next/link
のLinkコンポーネントを利用できていましたが、9系以降は外部リンクにLinkコンポーネントを利用すると、Invalid href passed to router
というエラーが吐かれるようになりました。
(表示されるエラー: https://github.com/vercel/next.js/blob/master/errors/invalid-href-passed.md)
これを受けて、外部リンクについてはaタグを利用するように修正しました。const LinkToGoogle: React.FC = () => ( - <Link href={'https://google.com'}> - <a>Google</a> - </Link> + <a href="https://google.com">Google</a> )useRouterのジェネリクス経由でのrouter.queryの型指定を廃止
ver8系以前は
next/router
からインポートしたuseRouter
に対して、ジェネリクス経由でrouter.query
の型を指定する方法が利用できました。
たとえばimport { useRouter } from 'next/router' const QueryText: React.FC = () => { const query = useRouter<QueryType>().query return ( <p>query.text</p> ) }みたいな使い方です。
このジェネリクス経由でのクエリ型指定が廃止され、返却されるqueryの型はParsedUrlQuery
という型で統一されることになりました。
型の内容はstring | string[] | undefined
なのでさもありなんという感じですが、「数値の文字列であることを保証したい」などの場合には型ガードなどで対応する必要がありそうです。型ガードでクエリの型の保証をするしないに関わらず、今回のver9化ではジェネリクス経由での型指定を削除する必要があります。
- const query = useRouter<QueryType>().query + const query = useRouter().querydynamic importのloadingオプションの初期値変更に伴う修正
Next.jsのdynamic importを利用している場合、コンポーネントのロードが完了するまでの間に表示されるコンポーネントを示す
loading
オプションの初期値が、() => null
に変更されました。
確か以前は() => loading...
とかだった気がしますが、これを回避するためにオプションでloading: () => null
と指定していた箇所については、オプションを削除することが可能になります。import dynamic from 'next/dynamic' const DynamicImported = dynamic( () => import('src/component/DynamicImported').then(mod => mod.DynamicImported), - { loading: () => null } )WithRouterPropsのimport先の変更
next/router
からインポートするwithRouter
でラップしたコンポーネントに渡るPropsの型定義ですが、こちらの型定義がnext/router
からnext/dist/client/with-router
に変更されたため、インポート先を修正する必要があります。- import { withRouter, WithRouterProps } from 'next/router' + import { withRouter } from 'next/router' + import { WithRouterProps } from 'next/dist/client/with-router'_app.tsxにおけるContainerコンポーネント利用の廃止
表題の通りです。以下のように修正します
src/pages/_app.tsximport React from 'react' import App, { Container } from 'next/app' class MyApp extends App { render() { const { Component, pageProps } = this.props - return ( - <Container> - <Component {...pageProps} /> - </Container> - ) + return <Component {...pageProps} /> } } export default MyApp参照: https://github.com/vercel/next.js/blob/master/errors/app-container-deprecated.md
viewPortを設定するmetaタグをpages/_documentからpages/_appコンポーネントに移行
以下のエラーそのままです。
https://github.com/vercel/next.js/blob/master/errors/no-document-viewport-meta.mdNext.js ver9の型定義に則り、Pagesコンポーネントの型を整備
Next.js ver9系のPagesコンポーネントの型定義は、概ね以下のような感じで定義できます。
import * as React from 'react' import { NextPage, NextPageContext } from 'next' import { connect } from 'react-redux' import { ParsedUrlQuery } from 'querystring' const TestPage: NextPage<PropsType, InitialPropsType> = ({ stateItem, query, fromAppOrDocumentItem, initialProps }) => ( <div> {stateItem} {query} {fromAppOrDocumentItem} {initialProps} </div> ) TestPage.getInitialProps = async ({ query }: NextPageContext) => { const initialProps = !!query return {query, initialProps} } type FromAppOrDocumentItem = { fromAppOrDocumentItem: boolean } type PropsType = ReturnType<typeof mapStateToProps> & FromAppOrDocumentItem & InitialPropsType type InitialPropsType = { query: ParsedUrlQuery initialProps: boolean } type StatesType = { stateItem: string } const mapStateToProps = ({ stateItem }: StatesType) => ({ stateItem }) export default connect(mapStateToProps)(TestPage) // propsの流れるイメージ図 /** * <App>/<Document> * ↓ * (connect Store) * ↓ * ↓ → → → → → → → → → → → → * ↓ ↓ * ↓ getInitialProps * ↓ ↓ * ↓ ← ← ← ← ← ← ← ← ← ← ← ← * ↓ * NextPage */これに則り、既存のPagesコンポーネントの型を整備してゆきました。
DynamicRouting対応
例えばver8.1の頃は
- src/ - pages/ - companySearch.tsx - companyDetail.tsx - companyAnswers.tsx - companyAnswerDetail.tsx - companyJobs.tsx - companyOccupations.tsx - companyOccupationDetail.tsxのように、ページの概要を表現した名前のファイルが同階層に並ぶpagesディレクトリがあり、加えて
src/server.js
に定義するカスタムサーバーにてsrc/server.jsserver.get('/companies/:id', (req, res) => { const id = req.params.id if (isValidCompanyId(id)) { app.render(req, res, `/companyDetail`, { companyId: id }) } else { res.status(404).end() } })のように、リクエスト先のURLを見て各pagesコンポーネントにリクエストを振り分ける、というような実装をしていました。
これを、DynamicRoutingを導入することで- src/ - pages/ - companies/ - [companyId].tsx - [companyId]/ - answers.tsx - answers/ - [answerId].tsx - occupations.tsx - occupations/ - [occupationId].tsx - search.tsxというように、URLの構造と対応するディレクトリ構造のPagesコンポーネントファイルを配置すれば、
[]
で囲ったパスに該当するリクエスト先URLのパスをNext.jsが解析し、Pagesコンポーネント側でrouter.query
からパスパラメータを引けるようになります。
こういった修正を通して、リクエストのPagesコンポーネントへの振り分けを開発スコープから外せる点を期待して、DynamicRoutingを導入することにしました。ファイル名の変更
DynamicRoutingに対応するページをURLに対応する形にrenameします
$ mkdir src/pages/companies $ mv src/pages/companyDetail.tsx src/pages/companies/[companyId].tsxカスタムサーバーのパス振り分け処理の削除
カスタムサーバーからDynamicRoutingに対応するパスへの振り分け処理を削除
src/server.js- server.get('/companies/:id', (req, res) => { - const id = req.params.id - if (isValidCompanyId(id)) { - app.render(req, res, `/companyDetail`, { companyId: id }) - } else { - res.status(404).end() - } - })
Linkコンポーネントのhrefプロパティの修正
DynamicRoutingの利用に伴い、Linkコンポーネントのプロパティの渡し方が変わります。
import Link from 'next/link' export const CompanyDetailLink: React.FC<{companyId: number}> = ({companyId}) => { - const href = `/companyDetail?companyId=${companyId}` + const href = '/companies/[companyId]' // pagesコンポーネントファイルのファイル名を指定 const as = `/companies/${companyId}` // パスパラメータを含んだパスを指定 return <Link href={href} as={as}><a>link</a></Link> }9.0.8 -> 9.1.7
Next.jsのバージョンアップグレード
package.json"dependencies": { ... - "next": "9.0.8", + "next": "9.1.7", ... },9.1.7 -> 9.3.6
ver 9.2 公式アップグレードブログ
ver 9.3 公式アップグレードブログ※ 一度ver9.2系最新(当時)の9.2.2に上げたのですが、IE11でポリフィルが適用されないバグがあり、ver9.3系では対応されているとのことだったので、9.1.7から9.3.6に上げることにしました。
built-in CSS Support対応
転職会議の当リポジトリでは@zeit/next-cssを利用してcssのインポートを行なっていましたが、ビルトインのCSS(モジュール)のサポートが追加されたことにより、
next-css
を消すことができました。ビルトインCSSサポートに乗ると、global CSSのimportをできる箇所は
pages/_app.tsx
に限定されます。
なので、他の箇所でglobal CSSをimportしている場合は、pages/_app.tsx
に集約する必要があります。moduled CSSのファイル名の修正
cssモジュール用の設定が不要になる一方で、Next.jsのビルトインCSSサポート上でcss moduleを利用しようとすると、ファイル名を
{任意のファイル名}.module.css
という形式にする必要があります。
ワンライナースクリプト等で一括変更できればよかったのですが、パッとわからなかったので330ファイルほどを手作業でrenameしました。next.config.jsからCSSモジュール用の設定を削除
next.config.js- const withCss = require('@zeit/next-css') ... - const cssConfig = [ - withCss, - { - cssModules: true, - cssLoaderOptions: { - importLoaders: 1, - localIdentName: '[local]___[hash:base64:5]' - } - } - ] ... module.exports = withPlugins( - [cssConfig, withOptimizedImages], + [withOptimizedImages], nextConfig )Next.jsのバージョンアップグレードおよびnext-cssへの依存削除
package.json"dependencies": { ... - "@zeit/next-css": "^1.0.1", - "next": "9.1.7", + "next": "9.3.6", ... },9.3.6 -> 9.4.4
Next.jsのバージョンアップグレード
package.json"dependencies": { ... - "next": "9.3.6", + "next": "9.4.4", ... },9.4.4 -> 9.5.5
next/linkのasプロパティの利用方法変更への対応
ver9.5.3からhrefに指定したパスを解析してpages/配下のファイルパスに当てはめてくれるようになったので、hrefに明示的にファイルパスを渡す必要がなくなりました。ただこの変更は後方互換性があるので、対応しなくてもバージョンを上げることは可能です。
// pages/companies/[companyId].tsx というファイルがある場合 - <Link href={"/companies/[companyId]"} as={"/companies/4075"}> + <Link href={"/companies/4075"}> <a>リブセンス</a> </Link>外部ドメインのCDNからアセットを取得する際に、arrow-originの設定がされていてもCORSエラーになるバグへの対応
報告されているissue にあるコメント通り、
next.config.js
にcrossOriginプロパティを追加します。next.config.jsconst nextConfig = { ... crossOrigin: 'anonymous', ... }Next.jsのバージョンアップグレード
package.json"dependencies": { ... - "next": "9.4.4", + "next": "9.5.5", ... },9.5.5 -> 10.0.3
Next.jsのバージョンアップグレード
package.json"dependencies": { ... - "next": "9.5.5", + "next": "10.0.3", ... },おわりに
以上でNext.jsのバージョンアップグレードは完了です。
next/imageやgetInitialProps -> getServerSidePropsへの移行、部分的なSSG、React17への対応など、まだ利用しきれていない機能も多いので、これからいろいろ試してみたいと思います。
- 投稿日:2020-09-24T18:38:29+09:00
【Gatsby.js】単一のjsonファイルを元に複数のページを生成する
Gatsby.jsを使って、jsonファイルから複数のページを生成する方法をまとめます。
Gatsby、情報自体はそこそこ豊富なんですが、日本語の情報が少ないですね。。記事データをまとめたjsonファイルを作成
$ mkdir src/data $ touch src/data/articles.jsonsrc/data/articles.json[ { "slug": "how-to-use-gatsby", "title": "【Gatsby.js】「ここだけ押さえれば普通に使える」って知識をまとめてみた", "content": "Gatsby.jsでシンプルな静的サイトを作る際に必要な知識だけをまとめました。CMSとの連携等については今回は扱いません。本文テキスト本文テキスト本文テキスト本文テキスト本文テキスト本文テキスト", "tags": ["React", "Gatsby"] }, { "slug": "learn-react", "title": "Reactの学習、今からやるならこうする", "content": "Reactの学習を初めて約2週間が経ちました。Railsの時と違って「とりあえずこれをやっておけば良い」ってものが見当たらずなかなか大変でしたが、試行錯誤の上色々と見えてきたのでまとめていきます。", "tags": ["React"] }, { "slug": "restore-mac", "title": "Macの初期化→リストアを良い感じにする", "content": "Brewfile、Mackup、Docker、Dropboxを使って良い感じにリストアできる環境を作ったので、まとめます。以下の記事をめちゃくちゃ参考にしました。macOSでの開発環境を全部Docker化したらリストア時間が1時間半になった", "tags": ["Docker", "Brewfile", "Mackup"] } ]記事のテンプレートページのベースを作成
$ mkdir src/templates $ touch src/templates/article.jssrc/templates/article.jsimport React from "react" export default () => ( <> <h1>タイトル</h1> <p>本文</p> <span>タグ1</span> <span>タグ2</span> </> )記事を生成する処理を記述
$ touch gatsby-node.jsgatsby-node.jsconst path = require("path") const data = require("./src/data/articles.json") exports.createPages = ({ actions }) => { const { createPage } = actions const template = path.resolve("./src/templates/article.js") data.forEach(article_object => { const path = `articles/${article_object.slug}` createPage({ path, // 生成されるページのpath component: template, // ページのベースとなるテンプレート context: article_object, // GraphQL経由で受け渡すデータのオブジェクト }) }) }GraphiQLを開き、データを取得するためのクエリを作成
gatsby develop
で開発サーバーを立ち上げ、ブラウザで http://localhost:8000/ にアクセスしてGraphiQLを開く。
allSitePage > edges > node > context
と順に開いていくと、title
、content
、tags
が見つかるのでチェックを入れて実行し、欲しいデータが取得できることを確認。GraphQLで取得したデータをテンプレートページに埋め込む
作成したクエリで必要なデータを取得し、テンプレートに埋め込みます。
GraphQLで取得したデータはdata
というオブジェクトに格納されるので、そこから値を取り出して出力します。src/templates/article.jsimport { graphql } from "gatsby" import React from "react" export default ({ data }) => { const article = data.allSitePage.edges[0].node.context return ( <> <h1>{article.title}</h1> <p>{article.content}</p> {article.tags.map(tag => ( <span>{tag}</span> ))} </> ) } export const query = graphql` query($path: String!) { allSitePage(filter: { path: { eq: $path } }) { edges { node { context { content tags title } } } } } `開発サーバーを再起動して確認
開発サーバーを再起動し、存在しないURLを入力して404ページを表示すると、ページ一覧に今作ったページが追加されていることが確認できます。
参考
- 投稿日:2020-09-24T17:41:21+09:00
[React] 初心者は必見でしょ。ルーティングのエラー解決と解説
どうもこんにちわ
今回は、Reactのルーティングを設定する上でよく目にする
Error: Invariant failed: You should not use <Link> outside a <Router>というエラーについて、僕自身調べてみて
Typescript中心のエラーの解決法が多いや、、。
と思ったので、シンプルなReactでの説明をしていきたいと思います。
また、解決に至るまでのプロセスで出会った3つの関数?(っていうのかな)たちも
どんどん解説していくので、ぜひ最後まで見ていってください。まず僕がこのエラーに引っかかった時のコードかこちら、
App.jsimport React from "react"; import { Link } from "react-router-dom"; export default function Home() { return ( <div className="container"> <title>Hands on Mania</title> <main className="u-text-center"> <h1 className="font-family-homemade">Hands on Mania</h1> <p className="description">studying</p> <Link to="/Auth/SignIn">signin</Link> </main> </div> ); }策① Memoryrouterを使う
最初は、react-router-domから「Memoryrouter」をインポートして
<Memoryrouter> <Link to="/Auth/SignIn">signin</Link> </Memoryrouter>このようにしましょう。との解決策でした
早速試したのですが、、結果は変わらず。。
まぁまぁ焦らず、ゆっくり行こーや。
ここで解決できてたらQiitaの記事にはしてません。では簡単に解説します。
Memoryrouterについて簡単に調べたところ、
テストおよびReact Nativeのような非ブラウザー環境で役立ちます。
と調べて一番上にあった記事で解説されていたので、
テストコードを書く時。または、ReactNative環境で普通は使うものなのかな?
と思いました。Memoryrouterと似たような(?)ものにhooksのuseMemoが思い出されたので、
こちらも申し訳程度に紹介。React公式によれば
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);このような公式で使い、あくまで最適化のために使うそうだ
初心者にはまだ早い領域かもしれないですね?策② Route と Switch を使う
こちらはgithubに乗っていた解決策だったので、
これはいけるっしょ。と内心ヨユーな表情で思っていたのですが、
あっけなく予想を裏切られました。
(僕のやり方が違う可能性が高い、、。)方法としては、このように記述するのさっ。というやり方です。
import { Route, Switch } from "react-router"; <Switch> <Route exact path="/Auth/SignIn" /> </Switch>しかし、結果は儚くとも同じようなエラー内容。。
Error: Invariant failed: You should not use <Switch> outside a <Router>だめだー、、。
俺の人生終わったー、、。諦めないで!
そうなるのはまだ早い。次で解決策を提示しながら一緒に解説するから、ちょっと落ち着け、モちつけ、、、、
解決策③ (これで解決!)
App.jsimport { BrowserRouter as Router, Route } from "react-router-dom"; import SignIn from "./Auth/SignIn"; <Router> <Header /> <Route exact path="/Auth/SignIn" component={SignIn} /> </Router>components/Header.jsimport { Link } from "react-router-dom"; <Link to="/Auth/SignIn">Signin</Link>今回はこれで解決しました!?
LinkとRouterをつないでいる感じですね
こちら先程の策と比べながら、今回大切にして欲しい
3つのポイント
を解説していきますね。
①react-routerとreact-router-domが似ているようでちょっと違う件についてまず、今回import したのはreact-router-dom です。
先程はreact-routerでしたね。
これは何が違うねん!
まぁまぁ一回落ち着け、モちつけ、、、、、、(お気に入りのボケです。)
簡単な違いはというと、
react-router-domはBrowserRouterをRouterと一緒に使い、
react-routerは historyをRouterと一緒に使うということです。詳しい解決や記述方法はreact-routerとreact-router-domこちらの記事を御覧ください!
まぁあまり大きな違いは無いようです。
② as の効果範囲
今回僕が BrowserRouter をimportする際に用いたasですが、
これの影響範囲は、例えばこの記述だと、どこまでだと思いますか?import { BrowserRouter as Router, Route, Switch } from “react-router-dom”;正解は、、、
Routerだけです。☆彡
以上。
③ componentsについて
<Route exact path="/Auth/SignIn" component={SignIn} />まず、exactの記述に関して
pathに指定したルートと全く同じところに移動する感じです。
exactをつけないと違うルートに行ってしまう可能性があるためつけたほうがいいです。
componentの記述について指定したconponentsを表示します。
今回はSignIn componentsを表示
表示したいcomponentsを import し忘れないように注意。以上で簡単なルーティングの設定方法の解説を終わりにします。
最後に、
この記事を見る限り、v3 から v4への移行期に、色々と新しいのが出たり、
古いのが消えていたりしているそうなので、念の為、検索する際は確認してみたほうが無難かもしれないですね
react-router v3からv4へのマイグレーション
BrowserRouter今回の参考文献【React】ルーティング設定方法
- 投稿日:2020-09-24T17:41:21+09:00
[React] ルーティング設置の仕方に悩んでいる方。ルーティングのエラー解決と解説
どうもこんにちわ
今回は、Reactのルーティングを設定する上でよく目にする
Error: Invariant failed: You should not use <Link> outside a <Router>というエラーについて、僕自身調べてみて
Typescript中心のエラーの解決法が多いや、、。
と思ったので、シンプルなReactでの説明をしていきたいと思います。
また、解決に至るまでのプロセスで出会った3つの関数?(っていうのかな)たちも
どんどん解説していくので、ぜひ最後まで見ていってください。まず僕がこのエラーに引っかかった時のコードかこちら、
App.jsimport React from "react"; import { Link } from "react-router-dom"; export default function Home() { return ( <div className="container"> <title>Hands on Mania</title> <main className="u-text-center"> <h1 className="font-family-homemade">Hands on Mania</h1> <p className="description">studying</p> <Link to="/Auth/SignIn">signin</Link> </main> </div> ); }策① Memoryrouterを使う
最初は、react-router-domから「Memoryrouter」をインポートして
<Memoryrouter> <Link to="/Auth/SignIn">signin</Link> </Memoryrouter>このようにしましょう。との解決策でした
早速試したのですが、、結果は変わらず。。
まぁまぁ焦らず、ゆっくり行こーや。
ここで解決できてたらQiitaの記事にはしてません。では簡単に解説します。
Memoryrouterについて簡単に調べたところ、
テストおよびReact Nativeのような非ブラウザー環境で役立ちます。
と調べて一番上にあった記事で解説されていたので、
テストコードを書く時。または、ReactNative環境で普通は使うものなのかな?
と思いました。Memoryrouterと似たような(?)ものにhooksのuseMemoが思い出されたので、
こちらも申し訳程度に紹介。React公式によれば
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);このような公式で使い、あくまで最適化のために使うそうだ
初心者にはまだ早い領域かもしれないですね?策② Route と Switch を使う
こちらはgithubに乗っていた解決策だったので、
これはいけるっしょ。と内心ヨユーな表情で思っていたのですが、
あっけなく予想を裏切られました。
(僕のやり方が違う可能性が高い、、。)方法としては、このように記述するのさっ。というやり方です。
import { Route, Switch } from "react-router"; <Switch> <Route exact path="/Auth/SignIn" /> </Switch>しかし、結果は儚くとも同じようなエラー内容。。
Error: Invariant failed: You should not use <Switch> outside a <Router>だめだー、、。
俺の人生終わったー、、。諦めないで!
そうなるのはまだ早い。次で解決策を提示しながら一緒に解説するから、ちょっと落ち着け、モちつけ、、、、
解決策③ (これで解決!)
App.jsimport { BrowserRouter as Router, Route } from "react-router-dom"; import SignIn from "./Auth/SignIn"; <Router> <Header /> <Route exact path="/Auth/SignIn" component={SignIn} /> </Router>components/Header.jsimport { Link } from "react-router-dom"; <Link to="/Auth/SignIn">Signin</Link>今回はこれで解決しました!?
LinkとRouterをつないでいる感じですね
こちら先程の策と比べながら、今回大切にして欲しい
3つのポイント
を解説していきますね。
①react-routerとreact-router-domが似ているようでちょっと違う件についてまず、今回import したのはreact-router-dom です。
先程はreact-routerでしたね。
これは何が違うねん!
まぁまぁ一回落ち着け、モちつけ、、、、、、(お気に入りのボケです。)
簡単な違いはというと、
react-router-domはBrowserRouterをRouterと一緒に使い、
react-routerは historyをRouterと一緒に使うということです。詳しい解決や記述方法はreact-routerとreact-router-domこちらの記事を御覧ください!
まぁあまり大きな違いは無いようです。
② as の効果範囲
今回僕が BrowserRouter をimportする際に用いたasですが、
これの影響範囲は、例えばこの記述だと、どこまでだと思いますか?import { BrowserRouter as Router, Route, Switch } from “react-router-dom”;正解は、、、
Routerだけです。☆彡
以上。
③ componentsについて
<Route exact path="/Auth/SignIn" component={SignIn} />まず、exactの記述に関して
pathに指定したルートと全く同じところに移動する感じです。
exactをつけないと違うルートに行ってしまう可能性があるためつけたほうがいいです。
componentの記述について指定したconponentsを表示します。
今回はSignIn componentsを表示
表示したいcomponentsを import し忘れないように注意。以上で簡単なルーティングの設定方法の解説を終わりにします。
最後に、
この記事を見る限り、v3 から v4への移行期に、色々と新しいのが出たり、
古いのが消えていたりしているそうなので、念の為、検索する際は確認してみたほうが無難かもしれないですね
react-router v3からv4へのマイグレーション
BrowserRouter今回の参考文献【React】ルーティング設定方法
- 投稿日:2020-09-24T17:12:59+09:00
Drupalize.MeのReact & Drupal 8 Code Examplesをddevで起動する
これを起動する:https://github.com/DrupalizeMe/react-and-drupal-examples
バックエンド(Drupal 8)を起動
事前にcomposer、docker,ddevなど必要なライブラリをインストールしておく。
$ cd drupal $ ddev start $ ddev composer install $ ddev import-db --src=./backup.sql.gzブラウザで https://react-tutorials-2.ddev.site にアクセスし、初期インストールを行う。
インストールプロフィールは standard、データベース情報は以下の通り入力する。これは drupal/.ddev 内で設定されているもので、
$ ddev start
ですでに起動している。
項目 値 database db username db password db host db port 3306 最後に
view existing site
をクリックしてDrupalサイトに移動し、コンテンツやユーザー、構成などがバックアップデータベースから反映されているか確認。ユーザー名:admin、パスワード:admin で設定されているユーザーが存在するのでそれで管理者としてログインしてみる。https://react-tutorials-2.ddev.site/admin/config/people/simple_oauth に移動して、
generate keys
をクリックした後、drupal/keys ディレクトリに private.key と public.key が生成されているか確認。フロントエンド(React)を起動
$ cd react-decoupled $ yarn install $ yarn run startブラウザで http://localhost:3000 にアクセスし、以下の画像のようにDrupalの情報が反映されているか確認。
- 投稿日:2020-09-24T00:51:55+09:00
初Reactでも能力Amplifyしてスマートな(?)チーム分けアプリを作ってみる
突然ですが現withコロナ時代においてチーム分け時が必要な時はないでしょうか。
例えば最近増えましたリモート会議ですと一人しか同時に話せないのでzoomのブレイクアウトルーム使うなどチームを分割することは良くあるのかなと思ってます
(ちなみに自分の所属するチームでは毎朝、朝会でランダムに2、3人のチームを組んで開発しております。Be Full Stackです。)普通のチームわけであればrandomに割り振ればいいかと思いますが、下記のような状況で困ることがありました
- なるべく均等に分けたいグループがある(たとえば下記)
- 研究室MTGで各学年が均等に分かれて欲しい
- 飲み会で1つのチームに各世代の人がいて欲しい
- マルチファンクショナルなスクラムチームを結成する時に各分野(インフラ、バックエンド、フロントエンド、デザイナー、QA)の人がそれぞれれのチームに入って欲しい)
- ランダムだと前回と同じ人と同じチームになってなかなか同じチームになれない人がいる
このようなことが何回かあって毎回手動で調整するのが面倒だなと思い、楽するために苦労するエンジニア精神でこの2つの問題を解消するwebアプリ作ってみました。
↓作ったもの
https://www.memo-team-gen.com/↓
クソースコード (拙いですが公開しました。aws-exports.js
などセキュリティ的にまずそうなのは除いております。)
https://github.com/LittleWat/memorizing-team-generator-public普段の業務はモバイルアプリなのでiOSやAndroid,バックエンドをいじることはあってもフロントをいじることはほとんどなく、フロントエンドは 状態だったのですがこれを良い機会にということでググり力 で開発しました。
時代はjsよりtsということで、初めて
typescript
を使ったので設定周りでつまづきまくりました。が、何とかバックもフロントも開発できバックもフロントもイケるts(js)の旨味を享受しました。typescript
初めてではありましたが、null安全で型推論が多少ありmapはじめコレクションの処理がサポートされていたりしてswift
,kotlin
と似ており個人的に書きやすかったです。いろいろググると、
React
,Vue
,Angular
がフロントエンド の三大レームワークとしてあり、Angular
は後方互換性を消しまくってて開発辛いみたいな記事を散見したので選択肢から外し、React
とVue
で迷い 世界的にメジャーなのはReact
ということでReact
の学習から入りました。(のちのちVue
のチュートリアルも行ったのですが、今回のようにRedux使わなくても良さそうな小さいアプリだったらVue
のほうが楽かもと感じました。)では開発の流れや内容についてご紹介したいと思います。
開発の流れ
Reactの学習
適当にググって出るチュートリアルをやっているとReactは書き方が
class
のものとfunction
(Hooks
使うもの)のものの大きく二種類があるのを知り、Hooks
がナウいことをしったので何かまとまっている良さげな記事ないかなと調べておりますと下記の良記事に遭遇できました。こちらで紹介されていた本(pdf)を買って勉強しました。
こちらの本、体系的にまとまっててお世話になりました
Boothのpdfの本ですが、紙の本は時代の流れについていけてない(hooks以前のものが多い)ので流行りすたり(競争)の激しいDeepLearing研究時代を彷彿とさせるものを感じました。またUIのライブラリはbootstrapか何か使おうか迷っていましたが、material-ui がぱっと見充実しており、GoTのキャラが使われていたためmaterial-uiを使うことにしました。話それますが、GoTのキャラはDeepLearning研究でもこちらなどちらほら題材として使われていましたね。
開発
ナウい(お財布に優しい)構成で行きたいなと思い、firebaseは以前iOSアプリで一通り触ってみたので、今回は別のものがいいなということでawsのamplifyでサーバレスにいくことにしました。
フロントエンド
amplify hosting
で行いました。masterにpushするとデプロイされる仕組みも簡単に構築でき、ドメイン名の取得も簡単に行えました。amplify楽で良いですね。バックエンド
amplify api
はGraphQL
とRestfulAPI
の二種類選べます。ナウさを求める私はGraphQLで行おうと思い、下記のAWS公式のチュートリアルを行いました。
このチュートリアル、実装物も具体的で図解もあり、分かりやすく勉強になりました(おすすめです!!)
AppSync使えば簡単にElastic Seachと連携できるというのも知りました。強いですね。がしかし、実際のチームわけのモデルをしたところ、お金のかかる(CapacityUnitの料金が倍になる)DynamoDBのGlobasSecondaryIndexが自動で貼られてしまったり、思ったように動かなかったので断念しました。(さらなるGraphQLの勉強の必要性を感じました。。)
仕方がないので、ここで方向転換して普通の
RestfulAPI
でいくことにしました。こちらもいろいろ選べたのですが、
express
で開発することにしました。
フロントはcreate-react-app
でtypescirptが選べたのですが、express
はデフォルトがjs
でバックエンドもどうせなら統一してts
でいきたいなと思ったのですが、割と設定でハマったので後述してます。結論として何とかjsに変換してデプロイはできました。
(amplify、デフォルトをjs
かts
か選べるようになることを期待してます。(自分でコミットせぇという話かもですが汗))DynamoDBのテーブル設計
Dynamo(NoSQL)なのでRDSではアンチパターンとされる配列を格納しても良いかと思い、配列を格納する設計にしました。
設計にはNoSQL Workbench という下記のようなツールを用いました。
このツール、dynamoのlocalやawsに簡単に設計したものを反映でき、テーブル定義のjsonも保存できて使いやすかったです^^
チームわけのアルゴリズム
random
かsmart
か選べるようにしました。
スマートな
と言ってますが、実は100回ランダムにチーム生成してその中から スコア(frequency)が低いものを選ぶというシンプルな方法です。スコア(frequency,
freq.
)の計算ですが、これまでのチームわけの履歴を見て最近チームになった人がいればいるほど高いみたいな計算してます。簡単に数式で示しますと
w_{ij} = \begin{eqnarray} \left\{ \begin{array}{l} 1 (member_i とmember_j が同じチームの時) \\ 0 (member_i とmember_j が異なるチームの時) \end{array} \right. \end{eqnarray} \\ N: 過去の何回分まで見るか(max10回分見るようにしてます) \\ M: メンバーの総数と置くと下記のような計算をしてます。
freq. = \sum_{k=1}^N \sum_{i=1}^M \sum_{j=1}^M \frac{1}{k}w_{ij} \\具体例で説明しますと下記のように均等に分けたいグループがあるときに
単純に直近10回で何回一緒のチームになったかが下記の隣接行列で、5回一緒になった人が数組ありますが、
下記の重み付けされた隣接行列で100(最大値を100にスケールしました)の組みは[麻生太郎、松本さん]ペアなので
次回は[麻生太郎、松本さん]が同じチームには絶対ならないだろうということでgenerationすると下記のようになり
たしかに[麻生太郎、松本さん]は同じチームにならず、いい感じでチーム分けできていることが伝わるかと思います。
ぜひ使っていただいて何かフィードバックいただけると幸いです。
Analytics
amplify add analytics
で一瞬でAmazon Pinpointが使えるようになります。簡単ですね。。
GoogleAnalyticsやFirebaseAnalyticsの置き換えになりそうでしょうか。まとめ
- Amplifyは現在進行形で発展中(awsのUIもupdateされてますね。)
- 個人開発は全部自分でやらないといけないので良い勉強になる(慣れない技術を使うと苦労も多いですが発見が多く楽しい)
- 隣接行列の可視化のライブラリ探してて偶然見つけた
apexcharts
楽しい将来的にやりたいこと
- ランダム生成はログイン不要にしたい
- 生成結果をslack通知
などありますがやるかは不明です。。
ハマったこと
いろいろあるのですが、誰かの参考になればと思い念のため残しておきます。
backendのfunctionを勝手にいじってうまくdeployできない
同じく困った人がいた模様
https://github.com/yayun-12/amplify-typescript
下記、私がミスしていたファイル構成(
src
にts
ファイルを置いた。)➜ memoteamgenec8c5c26 git:(add-lambda-team-gen-api) ✗ tree . -L 2 . ├── amplify.state ├── dist │ ├── app.js │ ├── app.js.map │ ├── config │ ├── index.js │ ├── index.js.map │ ├── latest-build │ ├── latest-build.zip │ ├── models │ ├── repository │ ├── service │ └── util ├── function-parameters.json ├── memoteamgenec8c5c26-cloudformation-template.json ├── package-lock.json ├── package.json ├── parameters.json ├── src │ ├── app.ts │ ├── config │ ├── event.json │ ├── index.ts │ ├── models │ ├── repository │ ├── service │ ├── swagger.yml │ └── util └── tsconfig.jsonあるべき姿(デフォルトの姿): (
src
にjs
ファイル,package.json
を置く。)➜ memorizing-team-generator git:(add-lambda-team-gen-api) ✗ tree amplify/backend/function/tmp amplify/backend/function/tmp ├── amplify.state ├── function-parameters.json ├── parameters.json ├── src │ ├── app.js │ ├── event.json │ ├── index.js │ └── package.json └── tmp-cloudformation-template.json結局
src
の下にts
ディレクトリを切って下記のようにtsconfig.jsonを編集すると動いたが、ディレクトリ構造が気持ち悪いことになりました...
何か良い方法ないのですかねtsconfig.json{ "compilerOptions": { "outDir": ".", "sourceMap": true, "module": "commonjs", "target": "es6", "lib": ["es2017", "dom"], "moduleResolution": "node", "removeComments": true, "allowJs": true, "checkJs": true, "allowSyntheticDefaultImports": true, "baseUrl": "./ts", "paths": { "*": ["node_modules/*", "ts/types/*"] } }, "include": [ "ts/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ] }responsive designにできない
こちらは今もハマってます。ただのフロントエンドスキル不足故の問題です。。cssの勉強などします。。