できない.dev

React で「Objects are not valid as a React child」が解消できない

JSX の中括弧にオブジェクトや配列内オブジェクト、Promise、Date などを直接置くと React は描画できずこのエラーを投げる。
表示したい文字列や数値、React 要素に変換してから埋め込むのが基本。

#react#jsx#rendering#error#children

要約

Objects are not valid as a React child (found: object with keys {...}) は、JSX の {} にそのままオブジェクトを置いた ときに出る。
React が描画できるのは文字列・数値・React 要素・配列などで、プレーンオブジェクトや Promise、Date インスタンスは直接レンダリングできない。
エラーメッセージの with keys {...} 部分が、混入したオブジェクトの形を教えてくれる。

よくある原因

  1. オブジェクトを直接埋め込み: {user} と書いてしまう(正しくは {user.name}
  2. 配列要素がオブジェクト: {items} で配列の中のオブジェクトがそのまま展開される
  3. Promise を埋め込み: {fetchUser()} のように非同期関数の戻り値(Promise)を置いている
  4. Date / クラスインスタンス: {new Date()} や独自クラスを文字列化せず描画している

解決策

1. プロパティを取り出す

// NG
return <p>{user}</p>;
// OK
return <p>{user.name}</p>;

エラーの found: object with keys {name, age} を見れば、どのオブジェクトが入り込んだか特定できる。

2. 配列は map で要素化する

{items.map(item => (
  <li key={item.id}>{item.label}</li>
))}

配列をそのまま置くと中身のオブジェクトが描画対象になる。公式のリスト描画ガイド のとおり map で React 要素に変換し、key を付ける。

3. 非同期データは state 経由で

const [user, setUser] = useState(null);
useEffect(() => {
  fetchUser().then(setUser);
}, []);
return <p>{user?.name ?? "読み込み中"}</p>;

{fetchUser()} は Promise を描画しようとして失敗する。
解決済みの値を state に入れてから描画する。

4. オブジェクトは明示的に文字列化

<pre>{JSON.stringify(data, null, 2)}</pre>
<span>{date.toLocaleString()}</span>

デバッグ目的なら JSON.stringify、表示目的なら必要なプロパティだけを文字列として渡す。
JSX の {} に書けるのは式の評価結果が描画可能な値の場合だけ、という原則は 公式の JSX ガイド にある。

この記事は役立ちましたか?