Preact 8.x からのアップグレード
このドキュメントは、既存の Preact 8.x アプリケーションを Preact X にアップグレードする手順を説明することを目的としており、主に 3 つのセクションに分かれています。
Preact X には、Fragments、hooks、React エコシステムとの互換性の大幅な向上など、多くのエキサイティングな新機能が搭載されています。破壊的変更は可能な限り最小限に抑えるように努めましたが、機能セットを犠牲にすることなくすべてを完全に排除することはできませんでした。
依存関係のアップグレード
注: このガイド全体を通して、npm クライアントを使用します。コマンドは、yarn などの他のパッケージマネージャーにも簡単に適用できます。
それでは始めましょう!最初に Preact X をインストールします
npm install preactcompat がコアに移動したため、preact-compat はもう必要ありません。これで削除します
npm remove preact-compatPreact 関連ライブラリの更新
ユーザー(特にエンタープライズユーザー)に安定したエコシステムを保証するために、Preact X 関連ライブラリのメジャーバージョンアップデートをリリースしました。preact-render-to-string を使用している場合は、X で動作するバージョンにアップデートする必要があります。
| ライブラリ | Preact 8.x | Preact X |
|---|---|---|
preact-render-to-string | 4.x | 5.x |
preact-router | 2.x | 3.x |
preact-jsx-chai | 2.x | 3.x |
preact-markup | 1.x | 2.x |
Compat がコアに移動
サードパーティの React ライブラリを Preact で動作させるために、preact/compat を介してインポートできる**compat**ibility レイヤーを提供しています。以前は別パッケージとして提供されていましたが、調整を簡単にするためにコアリポジトリに移動しました。そのため、既存のインポートまたはエイリアス宣言を preact-compat から preact/compat に変更する必要があります(スラッシュに注意)。
スペルミスを導入しないように注意してください。一般的なミスとして、compat の代わりに compact と書くようです。それが難しい場合は、compat を react の `compatibility` レイヤーとして考えてください。そこから名前が来ています。
preact-cliを使用している場合は、このステップはすでに完了しています :tada
サードパーティライブラリ
破壊的変更の性質上、既存のライブラリの一部は X では動作しなくなる可能性があります。それらのほとんどは、ベータスケジュールに従ってすでに更新されていますが、そうでないものに遭遇する可能性があります。
preact-redux
preact-redux は、まだ更新されていないそのようなライブラリの 1 つです。良いニュースは、preact/compat が React に大幅に準拠しており、react-redux という React バインディングでそのまま動作することです。それに切り替えることで状況は解決します。バンドラーで react と react-dom を preact/compat にエイリアスしていることを確認してください。
preact-reduxを削除しますreact-reduxをインストールします
mobx-preact
react エコシステムとの互換性が向上したため、このパッケージは不要になりました。代わりに mobx-react を使用してください。
mobx-preactを削除しますmobx-reactをインストールします
styled-components
Preact 8.x は styled-components@3.x までしか動作しませんでした。Preact X では、この障壁はもうなく、最新バージョンの styled-components で動作します。react を preact に正しくエイリアスしていることを確認してください。
preact-portal
Portal コンポーネントは preact/compat の一部になりました。
preact-portalを削除しますpreact/compatからcreatePortalをインポートします
コードの準備
名前付きエクスポートの使用
ツリーシェイキングをより適切にサポートするために、Preact コアではデフォルトのエクスポートは提供されなくなりました。このアプローチの利点は、必要なコードのみがバンドルに含まれることです。
// Preact 8.x
import Preact from "preact";
// Preact X
import * as preact from "preact";
// Preferred: Named exports (works in 8.x and Preact X)
import { h, Component } from "preact";注: この変更は preact/compat には影響しません。react との互換性を維持するために、名前付きエクスポートとデフォルトエクスポートの両方が引き続き含まれています。
render() は常に既存の子を差分比較
Preact 8.x では、render() の呼び出しは常に要素をコンテナに追加していました。
// Existing markup:
<body>
<div>hello</div>
</body>
render(<p>foo</p>, document.body);
render(<p>bar</p>, document.body);
// Preact 8.x output:
<body>
<div>hello</div>
<p>foo</p>
<p>bar</p>
</body>Preact 8 で既存の子を差分比較するには、既存の DOM ノードを提供する必要がありました。
// Existing markup:
<body>
<div>hello</div>
</body>
let element;
element = render(<p>foo</p>, document.body);
element = render(<p>bar</p>, document.body, element);
// Preact 8.x output:
<body>
<div>hello</div>
<p>bar</p>
</body>Preact X では、render() は常にコンテナ内の DOM 子を差分比較します。したがって、コンテナに Preact でレンダリングされなかった DOM が含まれている場合、Preact はそれを渡された要素と比較しようとします。この新しい動作は、他の VDOM ライブラリの動作により近いものになります。
// Existing markup:
<body>
<div>hello</div>
</body>
render(<p>foo</p>, document.body);
render(<p>bar</p>, document.body);
// Preact X output:
<body>
<p>bar</p>
<div>hello</div>
</body>React の render メソッドの動作と完全に一致する動作が必要な場合は、preact/compat によってエクスポートされた render メソッドを使用します。
props.children は必ずしも array ではない
Preact X では、props.children が常に array 型であることは保証できません。この変更は、Fragments と子配列を返すコンポーネントに関する解析の曖昧さを解決するために必要でした。ほとんどの場合、それに気づかないかもしれません。props.children で配列メソッドを直接使用する場合のみ、toChildArray でラップする必要があります。この関数は常に配列を返します。
// Preact 8.x
function Foo(props) {
// `.length` is an array method. In Preact X when `props.children` is not an
// array, this line will throw an exception
const count = props.children.length;
return <div>I have {count} children </div>;
}
// Preact X
import { toChildArray } from "preact";
function Foo(props) {
const count = toChildArray(props.children).length;
return <div>I have {count} children </div>;
}this.state に同期的にアクセスしない
Preact X では、コンポーネントの状態は同期的に変更されなくなります。これは、setState の呼び出し直後に this.state から読み取ると、以前の値が返されることを意味します。代わりに、コールバック関数を使用して、以前の値に依存する状態を変更する必要があります。
this.state = { counter: 0 };
// Preact 8.x
this.setState({ counter: this.state.counter + 1 });
// Preact X
this.setState(prevState => {
// Alternatively return `null` here to abort the state update
return { counter: prevState.counter + 1 };
});dangerouslySetInnerHTML は子の差分比較をスキップ
vnode にプロパティ dangerouslySetInnerHTML が設定されている場合、Preact は vnode の子の差分比較をスキップします。
<div dangerouslySetInnerHTML="foo">
<span>I will be skipped</span>
<p>So will I</p>
</div>ライブラリ作成者向けの注意事項
このセクションは、Preact X で使用するパッケージを管理しているライブラリ作成者を対象としています。作成者でない場合は、このセクションをスキップしても問題ありません。
VNode の形状が変更されました
次のプロパティの名前を変更/移動しました
attributes->propsnodeName->typechildren->props.children
できる限り努力しましたが、常に React 用に作成されたサードパーティライブラリでエッジケースが発生しました。この vnode 形状の変更により、発見困難なバグが多数なくなり、compat コードが大幅にクリーンになりました。
隣接するテキストノードが結合されなくなった
Preact 8.x では、最適化として隣接するテキストノードを結合する機能がありました。DOM を直接比較しなくなったため、これは X には当てはまりません。実際、X ではパフォーマンスが低下することがわかったため、削除しました。次の例を見てみましょう
// Preact 8.x
console.log(<div>foo{"bar"}</div>);
// Logs a structure like this:
// div
// text
// Preact X
console.log(<div>foo{"bar"}</div>);
// Logs a structure like this:
// div
// text
// text