📚

Next.js React TypeScript Docker エラー忘備録

2023/03/31に公開

こんばんは
ポートフォリオ作成時に発生したエラー及び、予期せぬ動作に対して、どのようにアプローチしたかを記述しています。
「エラーとその解決方法」というよりかは、動作をしただけです。
ベストプラクティスではないでしょうし、前提としての実装がおかしいということもあります。
私のような未経験の方は斜にかまえてみてもらえると幸いです。

環境

  • React 18.2.0
  • Next.js(SSR) 13.1.1
  • TypeScript 4.6.4
  • Jest, React Testing Librar
  • prettier, eslint
  • TailwindCSS, daisyUI
  • docker(node:16.15.0), docker-compose

build時にエラー

Error: Could not find a production build in the '/usr/src/app/.next' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id
  • docker-composeのコマンドでyarn buildをしてから yarn startをする必要があった
command: 'yarn build'

Error: Hydration failed because the initial UI does not match what was rendered on the server. Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

  • pタグがネストしていた
const isBusinessHourOrNot = (service: ServiceRateType) => {
  if (typeof service === "string") {
    return service;
  } else {
    return (
      <p>
        {service.plan} {service.rate}</p>
    );
  }
};


//pタグの中にpタグが...
<p className="text-sm">
  {isBusinessHourOrNot(hotel.stayRates)}
</p>

Warning: Prop style did not match. Server: "display: block; max-width: 100%; width: initial; height: initial; background: none; opacity: 1; border: 0px; margin: 0px; padding: 0px …etc

  • Dark Readerというchromeの拡張機能を使ってnext/imageを使ってたら起きるエラー。拡張機能を切ったら消えた
  • DarkReaderはブラウザ内にソースを注入するため、事前にレンダリングされるSSG/SSRのツリーとブラウザでの最初のレンダリング時のツリーに相違がおきたため。本番環境では起きない。

How do I remove warning "Warning: Prop style did not match" when I import Image component in nextjs? · Discussion #40648 · vercel/next.js

react-simple-star-ratingで星のsvgが縦に並んでしまう

  • 要素のclass名をchrome devtoolsで調べて、直接CSSを当てて解決
.star-svg {
  position: relative;
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  vertical-align: middle;
  user-select: none;
  touch-action: none;
}

Error was not TypeError: (0 , _formatUrl).formatWithValidation is not a function

  • docker-composeのvolumeでバインドマウントしていた。

  • node_modulesを含めた全てのファイルをホストマシンとコンテナマシンでバインドマウントしていたので、コンテナUP中にコンテナ内部で更新してホストマシンもそれに同期されていた。

  • 本番環境では動作するが、開発環境では何も表示されない。dockerのログはevent - compiled client and server successfully in 2.4s (167 modules) が通常通り表示されている

  • react-simple-star-ratingをdocker-compose up中にrunで入れてからこのエラーが出たので依存関係がおかしいと思い以下を実行した。

yarn cache clean

rm -rf node_modules yarn.lock && yarn install

すると今度は

warn - Attempted to load @next/swc-linux-x64-gnu, but it was not installed

warn - Attempted to load @next/swc-linux-x64-gnux32, but it was not installed
warn - Attempted to load @next/swc-linux-x64-musl, but it was not installed

error - Failed to load SWC binary for linux/x64, see more info here: https://nextjs.org/docs/messages/failed-loading-swc

といったログがでて起動が中断された

  • next js のversionを12.2にダウングレードしたら解決したというIssueから、Next jsのversionが関連していると考え、最新versionにアップグレードしたら解決した。

yarn add next@latest react@latest react-dom@latest eslint-config-next@latest

This JSX tag's 'children' prop expects a single child of type 'ReactNode', but multiple children were provided.

  • 子をラップするフラグメント<>を間に入れることで解決
<div>
  {service.plan}
  {service.rate}{service.startTime}
</div>
<div>
  <>
    {service.plan}
    {service.rate}{service.startTime}
  <>
</div>

Warning: Each child in a list should have a unique "key" prop.

  • keyを設定していたつもりだが余計な<>が存在した。
// 間違い
{fields.map((field, index) => (
	<>
	  <tbody key={field.id}>
	    <tr key={field.id}>
	      <th>{index + 1}</th>
	      <td>
	<>

// 正しい
{fields.map((field, index) => (
  <tbody key={field.id}>
    <tr key={field.id}>
      <th>{index + 1}</th>
      <td>

react-responsiveのuseMediaQueryをNext.jsで使ったらError: Hydration failed because the initial UI does not match what was rendered on the server.

  • サーバーとクライアントでレンダー結果が異なるのでSSRでは意図した挙動にならない。
  • CSSのBreakPointでレスポンシブに対応

'describe' is not defined.eslintno-undef 'it' is not defined.eslintno-undef expect' is not defined.eslintno-undef

  • yarn add -D jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom をしてNext jsの公式通りjestのライブラリを入れた後に起きたエラー。eslintのプラグインとenvにjestを追加したら解決。

The default export is not a React Component in page

  • export default Home;のようにpages以下でファイルの最後にexportしたら解決。

useContextを使ってsetStateを更新するときにTypeError is not a function

  • そもそもContextAPIで使いたいコンポーネントをラップできていなかった。

TypeError: adapter is not a function

  • dockerの再起動で解決

error - AxiosError: connect EADDRNOTAVAIL ::1:80 - Local (:::0)

  • Mac再起動時に起きた
  • docker-compose buildで解決

This expression is not callable.

Type 'Boolean' has no call signatures.

  • useStateの更新関数が逆になっていた
// 間違い
const [setConfirmAlart, confirmAlart] = useState<boolean>(false);

// 正しい
const [confirmAlart, setConfirmAlart] = useState<boolean>(false);

AxiosでPromise { <pending> }が帰ってくる

  • awaitのつけ忘れ。
  • axios getはデフォルトでpromiseを返すのでawaitかaxios.get(url).then(res => { <resを使った処理> }) で取り出す必要がある。
  // 間違い
  const response =  client.get(`/auth/sessions`);
  // 正しい
  const response = await client.get(`/auth/sessions`);

Error: Your getServerSideProps function did not return an object. Did you forget to add a return?

  • try catchのcatchでreturnする必要があった
export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const { id } = ctx.query;

  try {
    const res = await client.get(`/hotels/${id}`);
    const hotelDetail: HotelDetailType = await res.data;
    return {
      props: {
        ...hotelDetail,
      },
    };
  } catch (error) {
    // 間違い
    console.log(error);

    // 正しい
    return {
      notFound: true,
    };
  }
}

The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

  • 2つ必要。
// 間違い
uid: ctx.req.cookies["_uid"] | "hoge"

// 正しい
uid: ctx.req.cookies["_uid"] || "hoge"

Refresh performing full reload

  • page以下でexportするコンポーネントを大文字にしたら動作
// 間違い
import React from "react";

const rate = () => {
  return <div>rate</div>;
};

export default rate;

// 正しい
import React from "react";

const Rate = () => {
  return <div>rate</div>;
};

export default Rate;

router.pushでダイナミックルートに遷移するときにリロードされる

  • formのonSubmitのデフォルトのイベントとキャンセルする必要があった
{/* 間違い */}
<form
  onSubmit={(e) => {router.push(`search?keyword=${searchWord}`);
}}>

{/* 正しい */}
<form
  onSubmit={(e) => {
    e.preventDefault();
    router.push(`search?keyword=${searchWord}`);
}}>

Error: Image Optimization using Next.js' default loader is not compatible with next export

  • next.jsはビルド時ではなく、ユーザーからのリクエストを受けたオンデマンド時に画像を最適化するため、デフォルトのローダーのままnext exportは出来ない。
  • 一つの解決策としては下記のようにして、ソース画像の画質、サイズ、フォーマットを変更することなくそのまま提供するように変更する。
module.exports = {
    experimental: {
        images: {
            unoptimized: true
        }
    }
}

CustomerError: Cannot read 'next' version in package.json

  • AmplifyでSSRを使う場合、package.jsonをnext buildのみにする
// エラー
"build": "next build && next export"

// 動作
"build": "next build"

Your app will appear here once you complete your first deployment.

  • バックエンドのapiを起動していなかった。

A client-side exception has occurred, see here for more

  • props.mapで展開していたが、propsが配列ではなかった。
  • develop環境で同じアクションをしたら、エラーが起きたので、それが原因だった。

Amplifyでデプロイしたサイトとlocalhostでproduction環境でのCSS,UIが違う

  • ブレイクポイントの設定で、mdは設定していたが、lgの設定をしていなかったため、サイトで開いたときに差異が生じた。
  • 特に、アスペクト比の異なる画像をfillで設定している場合、画像サイズも指定する必要があるが、relativeでwidhtとheightを指定している場合はlgの指定もしっかりしないと、UIが崩れる。

ESLint

ESLint couldn't find the plugin "eslint-plugin-jest".

  • eslint-plugin-jestをyarn addして解決
Oops! Something went wrong! :(

ESLint: 8.31.0

ESLint couldn't find the plugin "eslint-plugin-jest".

(The package "eslint-plugin-jest" was not found when loaded as a Node module from the directory "/usr/src/app".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

    npm install eslint-plugin-jest@latest --save-dev

The plugin "eslint-plugin-jest" was referenced from the config file in ".eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

error Command failed with exit code 2.

Array.prototype.map() expects a return value from arrow function

  • mapを使っているのにmapから生成される配列を使っていなかった。
  • 単純な繰り返し処理だけしたい場合はforEach。
services.map((service: HotelRateParams) => {
	postServiceListByWeekdays(service);
}),

services.forEach((service: HotelRateParams) => {
	postServiceListByWeekdays(service);
}),

Component definition is missing display name react/display-name

  • メモ化しているコンポーネントで発生。アロー関数を名前付きの関数に変更して解決。

React version not specified in eslint-plugin-react setting

  • eslintにreact detectを追加することで、自動的にReactのバージョンをピックアップできる
settings: {
    react: {
      version: "detect", 
    }

Jest

NextRouter was not mounted

  • テストファイルにuseRouterをモックする必要がある
jest.mock("next/router", () => ({ useRouter: jest.fn() }));

TypeError: Cannot read properties of undefined (reading 'mockResolvedValueOnce')

  • モック化に失敗している
  • 型アサーションを使ってjest.Mock型にキャストすることで解決
(getHoge as jest.Mock).mockResolvedValueOnce(mockResponse);

expect(received).toBeInTheDocument() received value must be an HTMLElement or an SVGElement. Received has type: object Received has value: {}

  • findはプロミスを返すので非同期で待つ必要がある
expect(screen.findByAltText("アバター")).toBeInTheDocument();

expect(await screen.findByAltText("アバター")).toBeInTheDocument();

Discussion