Preactアプリケーションのデバッグ
Preactにはデバックを容易にするツールが付属しています。それらはpreact/debug
にパッケージングされています。そして、preact/debug
をimport
することで使うことができます。
それらにはChromeやFirefoxやEdgeのブラウザ拡張であるPreact Devtoolsとの連携機能が含まれます。
<table>
要素のネストが間違っている等の間違いを見つけると警告やエラーを出力します。
- インストール
- デバッグ時の警告とエラー
- `undefined` parent passed to `render()`(`render()`に渡された親要素が`undefined`)
- `undefined` component passed to `createElement()`(`createElement()`に`undefined`がコンポーネントとして渡された)
- Passed a JSX literal as JSX twice(渡されたJSXリテラルがJSXとして2度評価された)
- Improper nesting of table detected(テーブルの不適切なネストが見つかりました)
- Invalid `ref`-property(無効な`ref`プロパティ)
- Invalid event handler(無効なイベントハンドラ)
- Hook can only be invoked from render methods(フックはrender()メソッドのみで実行することができる)
- Getting `vnode.[property]` is deprecated(`vnode.[property]`へのアクセスは非推奨です)
- Found children with the same key(子要素のkey属性が重複している)
インストール
Preact Devtoolsはブラウザのウェブストアでインストールすることができます。
インストールした後、preact/debug
をimport
して拡張との接続を初期化する必要があります。
このimport
はアプリケーション全体で一番最初に行われる必要があります。
@preact/preset-vite
はpreact/debug
を自動的に導入します。@preact/preset-vite
を使っている場合、次のステップをスキップして大丈夫です。
アプリケーションのメインエントリーファイルに以下のようにpreact/debug
をimport
します。
// 最初に`import`する必要があります。
import "preact/debug";
import { render } from 'preact';
import App from './components/App';
render(<App />, document.getElementById('root'));
Productionビルドからdevtoolsを削除する
ほとんどのバンドラでは必ず使われないif
文を見つけた場合、その分岐を削除します。
これを利用してdevelopmentビルド時のみpreact/debug
を含めることができます。そして、Productionビルド時には貴重なバイト数を節約することができます。
// 最初に`import`する必要があります。
if (process.env.NODE_ENV==='development') {
// `import`はトップレベルにのみ記述することができるため、ここではrequireを使います。
require("preact/debug");
}
import { render } from 'preact';
import App from './components/App';
render(<App />, document.getElementById('root'));
ビルドツールでNODE_ENV
変数が正しくセットされているか確認してください。
デバッグ時の警告とエラー
Preactが無効なコードを発見すると警告やエラーを表示することがあります。 それらを修正してアプリケーションを完璧にしましょう。
`undefined` parent passed to `render()`(`render()`に渡された親要素が`undefined`)
これはコードがアプリケーションをDOMノードではなく何も存在しないところにレンダリングしようとしていることを意味します。 両者の比較です。
// Preactが受け取った物
render(<App />, undefined);
// 期待しているもの
render(<App />, actualDomNode);
このエラーが発生する主な理由はrender()
関数が実行される際にDOMが存在していないからです。
存在することを確認して下さい。
`undefined` component passed to `createElement()`(`createElement()`に`undefined`がコンポーネントとして渡された)
Preactはコンポーネントの代わりにundefined
を渡すとエラーをスローします。
このエラーのよくある原因はdefault export
とnamed export
を取り違えていることです。
// app.js
export default function App() {
return <div>Hello World</div>;
}
// index.js: because `app.js`は`named export`を行っていないので動作しません。
import { App } from './app';
render(<App />, dom);
逆の場合も同じエラーがスローされます。
それはnamed export
と宣言してdefault export
を使おうとする場合です。
これを手早く確かめる方法は(エディタがまだそれを実行していない場合)import
したものを単にログに出力することです。
// app.js
export function App() {
return <div>Hello World</div>;
}
// index.js
import App from './app';
console.log(App);
// ログ: コンポーネントではなく { default: [Function] } が出力される
Passed a JSX literal as JSX twice(渡されたJSXリテラルがJSXとして2度評価された)
JSXリテラルもしくはコンポーネントをJSXに再度渡すことは無効です。それはエラーを引き起こします。
const Foo = <div>foo</div>;
// 無効: Fooは既にJSX要素です。
render(<Foo />, dom);
単に変数を直接渡すだけで修正することができます。
const Foo = <div>foo</div>;
render(Foo, dom);
Improper nesting of table detected(テーブルの不適切なネストが見つかりました)
HTMLにはテーブルの構造に対して非常に明確な決まりがあります。 それから外れるとデバッグすることが非常に難しいレンダリングエラーが発生します。 Preactはこれを見つけてエラーを出力します。 テーブルの構造について詳しく知りたい場合はMDNのドキュメントを読んでください。
Invalid `ref`-property(無効な`ref`プロパティ)
ref
プロパティに不適切な値が含まれている場合、このエラーがスローされます。
これには少し前に非推奨になった文字列ベースのref
プロパティも含まれます。
// 有効
<div ref={e => {/* ... */)}} />
// 有効
const ref = createRef();
<div ref={ref} />
// 無効
<div ref="ref" />
Invalid event handler(無効なイベントハンドラ)
うっかり間違った値をイベントハンドラに渡すことがあるかもしれません。
イベントハンドラに渡す値は常にfunction
もしくはnull
(削除したい場合)でなければなりません。
それ以外は無効です。
// 有効
<div onClick={() => console.log("click")} />
// 無効
<div onClick={console.log("click")} />
Hook can only be invoked from render methods(フックはrender()メソッドのみで実行することができる)
このエラーはコンポーネントの外でフックを使用したときに発生します。 フックは関数コンポーネントの内側でのみサポートされています。
// 無効: コンポーネント内で使う必要があります。
const [value, setValue] = useState(0);
// 有効
function Foo() {
const [value, setValue] = useState(0);
return <button onClick={() => setValue(value + 1)}>{value}</button>;
}
Getting `vnode.[property]` is deprecated(`vnode.[property]`へのアクセスは非推奨です)
Preact Xは内部のvnode
のプロパティ名に互換性を破壊する変更を加えました。
Preact 8.x | Preact 10.x |
---|---|
vnode.nodeName |
vnode.type |
vnode.attributes |
vnode.props |
vnode.children |
vnode.props.children |
Found children with the same key(子要素のkey属性が重複している)
仮想DOMベースのライブラリは、子要素の移動を検知する必要があります。 そのために、どの子要素がどれであるかを知るための情報が必要です。 これは動的に子要素を生成する場合にのみ必要です。
// 両方の子要素が同じ"A"keyを持っています。
<div>
{['A', 'A'].map(char => <p key={char}>{char}</p>)}
</div>
それを行う正しい方法はユニークなキーを付与することです。
ほとんどの場合、反復処理の対象になるデータは何らかの形でid
を持っているはずです。
const persons = [
{ name: 'John', age: 22 },
{ name: 'Sarah', age: 24 }
];
// コンポーネントのrender部分
<div>
{persons.map(({ name, age }) => {
return <p key={name}>{name}, Age: {age}</p>;
})}
</div>