T-CREATOR

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

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

もっと見る