0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】罠でしかないかと思われた npx next がエラー発見の手柄を立てた回

Posted at

以前、こんな記事を書いた。

npx next は罠!非推奨! #TypeScript - Qiita

npx next コマンドで立てることができるサーバーではページの表示が遅く、npx next build では発見できるエラーを発見できないことがあるため、使わないほうがいいと主張する記事である。

しかし、先日、逆に npx next build では見逃し、npx next では指摘してくれるエラーを発見した。
今回はこれを紹介する。

間違っているコード

mikecat/error-spotted-by-npx-next: npx next build で発見できなかったエラーを npx next が発見する回

action.ts
'use server';

export async function greetAction(name: string): Promise<string> {
  return `Hello, ${name}!`;
}
page.tsx
'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import { greetAction } from './action';

function getGreeting(name: string): Promise<string> {
  return greetAction(name);
}

function GreetingDisplay({ greetingPromise } : { greetingPromise: Promise<string> }) {
  const [greeting, setGreeting] = useState('');

  useEffect(() => {
    greetingPromise.then((result) => {
      setGreeting(result);
    }, (error) => {
      console.error(error);
    });
  }, [greetingPromise]);

  return (
    <p>{greeting}</p>
  );
}

export default function Home() {
  const [name, setName] = useState<string | null>(null);
  const nameInputRef = useRef<HTMLInputElement>(null);

  const setNameHandler = useCallback(() => {
    if (nameInputRef.current) setName(nameInputRef.current.value);
  }, []);

  return (
    <>
      <form action={setNameHandler}>
        <p>
          <input type='text' ref={nameInputRef} defaultValue='' size={20} />
          <button type='submit'>send</button>
        </p>
      </form>
      {name !== null ? (
        <GreetingDisplay greetingPromise={getGreeting(name)} />
      ) : null}
    </>
  );
}

これは、入力欄に文字列を入れてボタンを押すと、それを Server Actions を用いて加工し、結果を表示するプログラムである。
npx next build でビルドを行い、npx next start でサーバーを起動し、Firefox でアクセスして操作を行った結果、エラーは出ずに意図した表示となった。

実行の様子 (クライアント)

ビルド時、および実行時のサーバーでも、エラーは出ていない。

実行の様子 (サーバー)

npx next で実行した際のエラー

このコードを npx next で立てたサーバーで実行すると、以下のエラーが出た。

Cannot update a component (`Router`) while rendering a different component (`Home`). To locate the bad setState() call inside `Home`, follow the stack trace as described in https://react.dev/link/setstate-in-render

npx-next-gj-sample-20251026-3.png

パッと見、今回書いたコードでは Router なんてコンポーネントは定義しておらず、意味不明なメッセージに思える。

とはいえ、よく考えると、レンダー時に getGreeting 関数を呼び出しており、これが Server Actions である関数 greetAction を呼び出しているので、「レンダー時に副作用を起こしてはいけない」という原則に違反していることがわかる。

エラーの解消

今回のエラーは、getGreeting 関数の呼び出しを useEffect で実行される関数の中に移動することで解消できる。

mikecat/error-spotted-by-npx-next at fix

page.tsx
'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import { greetAction } from './action';

function getGreeting(name: string): Promise<string> {
  return greetAction(name);
}

function GreetingDisplay({ name } : { name: string }) {
  const [greeting, setGreeting] = useState('');

  useEffect(() => {
    const greetingPromise = greetAction(name);
    greetingPromise.then((result) => {
      setGreeting(result);
    }, (error) => {
      console.error(error);
    });
  }, [name]);

  return (
    <p>{greeting}</p>
  );
}

export default function Home() {
  const [name, setName] = useState<string | null>(null);
  const nameInputRef = useRef<HTMLInputElement>(null);

  const setNameHandler = useCallback(() => {
    if (nameInputRef.current) setName(nameInputRef.current.value);
  }, []);

  return (
    <>
      <form action={setNameHandler}>
        <p>
          <input type='text' ref={nameInputRef} defaultValue='' size={20} />
          <button type='submit'>send</button>
        </p>
      </form>
      {name !== null ? (
        <GreetingDisplay name={name} />
      ) : null}
    </>
  );
}

結論

npx next にはページの表示が遅い、検出されないエラーがあるなどの問題があるものの、逆に npx next では検出されるが npx next build では検出されないエラーがあることがわかった。
npx next build だけに頼るのではなく、時々は npx next で実行してのチェックも行うべきなようだ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?