(Reactから)Preactへの移行

preact/compatはReactのエコシステムに存在する多くのライブラリをPreactで使用することができるようにする互換レイヤです。既存のReactアプリケーションをPreactに移行する場合はこれを使うことをお勧めします。

preact/compatを使うことによってワークフローやコードベースを変更することなく既存のReact/ReactDOMで作られたコードで開発し続けることができます。 preact/compatはバンドルサイズを2kB増加させますが、npmにあるほとんどのReact用のモジュールを利用できるようになるという利点があります。 preact/compatパッケージは、reactreact-domと同じような動作するようにPreactコアに調整を加えたものです。



compatの設定

preact/compatを設定するにはreactreact-dompreact/compatにエイリアスする必要があります。 バンドラでエイリアスする方法を詳しく知りたい場合ははじめにを読んでください。

PureComponent

PureComponentクラスはComponentクラスと似た動作をします。 違いは新しいpropsが古いpropsと等しい場合、PureComponentはレンダリングをスキップする点です。 この判断は、古いpropsと新しいpropsのそれぞれのプロパティが参照的に等しいかどうか浅い(shallow)比較をすることにより行います。 これによって不要な再レンダリングを避けることができ、アプリケーションは大幅にスピードアップします。 PureComponentshouldComponentUpdateのデフォルト実装を提供することにより、これを実現しています。

import { render } from 'preact';
import { PureComponent } from 'preact/compat';

class Foo extends PureComponent {
  render(props) {
    console.log("render")
    return <div />
  }
}

const dom = document.getElementById('root');
render(<Foo value="3" />, dom);
// Logs: "render"

// 2回目のレンダリングではログを出力しません。
render(<Foo value="3" />, dom);

PureComponentはレンダリングコストが高い場合のみ有効です。シンプルなDOMツリーの場合、propsを比較するより普通にレンダリングを実行するほうが速い場合もあります。

memo

memoは関数コンポーネント版のPureComponentに相当します。 デフォルトではPureComponentと同じ比較を行いますが、個別に比較を行う関数を設定することもできます。

import { memo } from 'preact/compat';

function MyComponent(props) {
  return <div>Hello {props.name}</div>
}

// デフォルトの比較を使用します。
const Memoed = memo(MyComponent);

// 比較を行う関数を使用します。
const Memoed2 = memo(MyComponent, (prevProps, nextProps) => {
  // `name`が変わった場合のみ再レンダリングします。
  return prevProps.name === nextProps.name;
})

memoに渡す比較関数は再レンダリングをスキップしたい場合はtrueを返します。shouldComponentUpdateは再レンダリングをスキップしたい場合はfalseを返します。両者の戻り値が逆なことに注意してください。

forwardRef

forwardRefを使うとコンポーネントの外部からコンポーネント内部の要素を参照することができます。

import { createRef, render } from 'preact';
import { forwardRef } from 'preact/compat';

const MyComponent = forwardRef((props, ref) => {
  return <div ref={ref}>Hello world</div>;
})

// refはMyComponentではなくMyComponent内部の`div`への参照を持ちます。
const ref = createRef();
render(<MyComponent ref={ref} />, dom)

これはライブラリの開発にとても役立ちます。

Portals

createPortal()を使うとレンダリング時にコンポーネントの外にあるDOM Nodeにレンダリング結果を加えることができます。 加える対象となるDOM Nodeはレンダリング時よりも前に存在している必要があります

<html>
  <body>
    <!-- Appはここにレンダリングされる -->
    <div id="app"></div>
    <!-- Modalsをここにレンダリングする必要がある -->
    <div id="modals"></div>
  </body>
</html>
import { createPortal } from 'preact/compat';
import MyModal from './MyModal';

function App() {
  const container = document.getElementById('modals');
  return (
    <div>
      I'm app
      {createPortal(<MyModal />, container)}
    </div>
  )
}

Preactはブラウザのイベントシステムを使用しているのでイベントがPortalコンテナを通じて他のツリーにバブルアップしないことを忘れないでください。

Suspense (実験的な機能)

Suspenseを使うとSuspenseの下に存在する子コンポーネントがロード中の場合はプレースホルダを表示することができます。 一般的なユースケースとして、レンダリングする前にネットワークからコンポーネントをロードする必要があるcode-splittingを行う場合が挙げられます。

import { Suspense, lazy } from `preact/compat`;

const SomeComponent = lazy(() => import('./SomeComponent'));

// 使い方
<Suspense fallback={<div>loading...</div>}>
  <Foo>
    <SomeComponent />
  </Foo>
</Suspense>

この例では、SomeComponentコンポーネントがロードされてPromiseがresolveするまで、UIにloading...というテキストを表示します。

この機能は実験的な機能でバグがあるかもしれません。テストで試せるようにプレビュー版に含めました。製品版では使用しないことをお勧めします。

Built by a bunch of lovely people ubitools.com