TypeScriptを使用したNext.js 5からNext.js 6へ移行した時行った対応について

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Next.jsはこのサイトでも使っているのですが
比較的容易にBFF(バックエンドフォーフロントエンド)が実現できるツールとして大変有用です。
UXを実現していくためにSPA(シングルーページアプリケーション)という
アーキテクトがあげられますが
単純なSPAとしての問題点であげられるのが
- リクエストの複雑化によるランドトリップ
- 初回ロード時間の遅延
このあたりの課題の解決としてBFFというアーキテクチャの設計思想があります。
そのためこれらの解決を実現していくために
このサイトではNext.js
を採用しました。
そんなNext.js6が5月にリリースされたため
マイグレーションのログとして内容をまとめました。
環境
- React 16.3.2
- Next.js 5.1
- typescript 2.8.3
terminal$ yarn upgrade --latest next next-redux-wrapper typescript
terminal$ yarn upgrade --latest @zeit/next-typescript @types/next
packageのバージョン
- next@5.1 → next@6.1.1
- next-redux-wrapper@1.3.7 → next-redux-wrapper@2.0.0-beta.6
- typescript@2.8.3 → typescript@2.9.2
- @zeit/next-typescript@0.1.1 → @zeit/next-typescript@1.1.0
- @types/next@2.4.8 → @types/next@6.0.3
next-redux-wrapper関連
アップデート後に出たエラー
terminal__WEBPACK_IMPORTED_MODULE_2_next_redux_wrapper__ is not a function
解決方法
書き方が変わりました。
next-redux-wrapper1系
getInitialPropsを呼び出したReact
クラスをwithRedux
でラップすることで
ステートを子コンポーネントで参照できます。
./pages/index.tsx
typescriptimport * as React from 'React';
import { createStore } from 'redux';
import * as withRedux from 'next-redux-wrapper';
import Page from './components/page';
const reducer = (state = {hoge: ''}, action) => {
switch (action.type) {
case 'HOGE':
return {...state, hoge: action.payload};
default:
return state
}
};
const makeStore = (initialState, options) => {
return createStore(reducer, initialState);
};
class App extends React.Component<object> {
public static getInitialProps({ store }: Context): void{
const state = store.getState();
await store.dispatch({type: 'Hoge', payload: 'hoge'});
}
public render(): JSX.Element {
return (
<Page />
);
}
}
export default withRedux(makeStore)(App);
受け取った props
を出力
./components/page.tsx
import * as React from 'React';
const Page = (props) => {
return <div>Prop from Redux {props.hoge}</div>
}
export default connect()(Page);
next-redux-wrapper2系
getInitialPropsを呼び出しreact-redux
に渡しProviderします
。
そのReact
クラスをwithRedux
でラップすることで
ステートを子コンポーネントで参照できます。
./pages/_app.tsximport * as React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App, { Container } from 'next/app';
import withRedux from 'next-redux-wrapper';
const reducer = (state = { foo: '' }, action) => {
switch (action.type) {
case 'FOO':
return { ...state, foo: action.payload };
default:
return state
}
};
const makeStore = (initialState, options) => {
return createStore(reducer, initialState);
};
class MyApp extends App<object> {
static getInitialProps({ Component, ctx }) {
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
return { pageProps };
}
render() {
const { Component, pageProps, store } = this.props;
return (
<Container>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</Container>
);
}
}
export default withRedux(makeStore)(MyApp);
単純に withRedux
の箇所を _app.tsx
に切り出す形になりましたが
pagesがかなりシンプルになりました
各ページごとにストアへ保存するデータを分けてかくケースについては
以前よりかなりスッキリ見やすくなっています。
./components/page.tsximport * as React from 'React';
import Page from './components/page';
class MyApp extends React.Component<object> {
public static getInitialProps({ store }: Context):void{
const state = store.getState();
store.dispatch({type: 'Hoge', payload: 'hoge'});
}
public render(): JSX.Element {
return (
<Page />
);
}
}
export default App;
コネクト関数については以前と同じ形で使用できます。
./pages/index.tsximport * as React from 'React';
const Page = (props: object) => {
return <div>Prop from Redux {props.hoge}</div>
}
export default connect()(Page);
@zeit/next-typescript関連
アップデート後に出たエラー
typescriptLoaderOptionsはサポートされなくなりました
terminalError: `typescriptLoaderOptions` in next.config.js is no longer supported. https://err.sh/next-plugins/typescript-loader-options
解決方法
以下を削除
next.config.js typescriptLoaderOptions: {
transpileOnly: false
}
'next / babel'モジュールを見つけることができません
terminalUnhandledPromiseRejectionWarning: Error: Cannot find module 'next/babel' from '/Users/XXXX/XXXX'
解決方法
.babelrcを作成し下記を記述
.babelrc{
"presets": [
"next/babel",
"@zeit/next-typescript/babel"
]
}
- article
Next.jsの開発中に発生する Warning: Prop `className` did not match. Server: Client: ...の解決策
- article
Next.jsとEdge Runtimeを組み合わせて超爆速サーバーレス表示を実現する方法
- article
Next.jsでSEO対策を強化するための5つのポイント + アンチパターンの回避策
- article
Next.js × Suspenseを活用した非同期ルーティング体験を実現する方法
- article
next/image vs 外部CDNを徹底比較!Next.js画像最適化戦略を考えてみた
- article
Next.js 「App Router」と「Pages Router」の違いと移行のコツ
- blog
技術的負債とどう向き合う?フロントエンドチームが事業成長と両立させるための現実的なロードマップ戦略
- blog
「目標設定、何書けば…」を解決!フロントエンドエンジニアのための OKR/MBO 具体的活用事例
- blog
もう「目標倒れ」しない!フロントエンドチームが達成感を得られる目標設定と進捗管理のコツ
- blog
メンバーの強みを引き出す!フロントエンドリーダーが行うべき「フィードバック」と「コーチング」の違い
- blog
1on1 が劇的に変わる!フロントエンドエンジニアの成長を加速させる「問いかけ」の技術
- blog
メンバーの主体性を引き出す!フロントエンドリーダーが実践する「任せる」勇気と仕組みづくり
- review
人生が激変!『苦しかったときの話をしようか』森岡毅著で発見した、本当に幸せなキャリアの築き方
- review
もう「何言ってるの?」とは言わせない!『バナナの魅力を 100 文字で伝えてください』柿内尚文著 で今日からあなたも伝え方の達人!
- review
もう時間に追われない!『エッセンシャル思考』グレッグ・マキューンで本当に重要なことを見抜く!
- review
プロダクト開発の悩みを一刀両断!『プロダクトマネジメントのすべて』及川 卓也, 曽根原 春樹, 小城 久美子
- review
ビジネスも人生も攻略!『行動経済学が最強の学問である』の衝撃
- review
神メンタル 「心が強い人」の人生は思い通り 星 渉