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"
]
}
articleNext.js でドキュメントポータル:MDX/全文検索/バージョン切替の設計例
articleNext.js でインフィニットスクロールを実装:Route Handlers +`use` で滑らかデータ読込
articleRedis 使い方:Next.js で Cache-Tag と再検証を実装(Edge/Node 両対応)
articleNext.js の RSC 境界設計:Client Components を最小化する責務分離戦略
articleNext.js ルーティング早見表:セグメント・グループ・オプションの一枚まとめ
articleNext.js × pnpm/Turborepo 初期構築:ワークスペース・共有パッケージ・CI 最適化
articleTypeScript satisfies 演算子の実力:型の過剰/不足を一発検知する実践ガイド
articlePlaywright × TypeScript 超入門チュートリアル:型安全 E2E を最短構築
articleTypeScript 型カバレッジを KPI 化:`type-coverage`でチームの型品質を可視化する
articleTypeScript 公開 API の型設計術:`export type`/`interface`/`class`の責務分担と境界設計
articleESLint を Yarn + TypeScript + React でゼロから構築:Flat Config 完全手順(macOS)
articleTypeScript 型縮小(narrowing)パターン早見表:`in`/`instanceof`/`is`/`asserts`完全対応
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleTauri で Markdown エディタを作る:ライブプレビュー・拡張プラグイン対応
articleStorybook で“仕様が生きる”開発:ドキュメント駆動 UI の実践ロードマップ
articleshadcn/ui で B2B SaaS ダッシュボードを組む:権限別 UI と監査ログの見せ方
articleSolidJS の Control Flow コンポーネント大全:Show/For/Switch/ErrorBoundary を使い分け
articleRemix で管理画面テンプレ:表・フィルタ・CSV エクスポートの鉄板構成
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来