WordPress開発では、独自のReactコンポーネントをテーマプラグインに統合し、動的でレスポンシブなユーザーインターフェースを作成することがあります。

React 19のリリースが間近に迫る今、既存のコードベースに影響を与える可能性のある新機能や非推奨の変更に備えておくことが重要です。7月16日にリリース予定のWordPress 6.6には、React 18.3が導入されています。このバージョンは18.2とほぼ同じになりますが、React 19リリースに向けた非推奨の機能に対する警告が追加されています。

非推奨となる機能への対処は、React 19との互換性を確保するために不可欠です。対処を怠ると、React 19がリリースされWordPressに組み込まれた際に、カスタムブロックやプラグイン、テーマにバグや問題が発生する可能性があります。

そこで今回は、非推奨となる機能の概要とコード例、機能をシームレスに維持するために非推奨機能を置き換える方法をご紹介します。

Reactで削除される非推奨機能

Reactライブラリを効率化し、ベストプラクティスを促進する目的で、一部非推奨APIと機能が削除されました。以下主な変更点、およびコード更新方法を見ていきます。

1. 関数コンポーネントdefaultPropsの削除

React 19では、関数コンポーネントのdefaultPropsが削除され、ES6のデフォルトパラメータが採用されます。WordPressチームによると、この非推奨関数はプラグインやテーマで最も使用されています。

中には、defaultPropsを使って関数コンポーネントのpropsにデフォルト値を提供し、特定のpropsが渡されなくてもコンポーネントが正しく動作するようにしているWordPress開発者もいるかもしれません。

以下は、defaultPropsを使用した現在のコードの例です。

function CustomButton({ label, color }) {
    return <button style={{ backgroundColor: color }}>{ label }</button>;
}

CustomButton.defaultProps = {
    label: 'クリック',
    color: 'blue',
};

CustomButtonコンポーネントは、defaultPropsによるデフォルトのlabelcolor値を持っています。React 19では警告エラーが投げられ、ES6のデフォルトパラメータを代わりに使用するように促します。

ES6のデフォルトパラメータを使って更新すると、以下のようになります。

function CustomButton({ label = 'クリック', color = 'blue' }) {
    return <button style={{ backgroundColor: color }}>{ label }</button>;
}

ES6のデフォルトパラメータを使用すると、デフォルト値が関数のシグネチャに直接記述されるためコードが読みやすくなり、メンテナンスも容易になります。

2. 関数コンポーネントpropTypesの削除

propTypesReact 15.5.0で非推奨となり、React 19で完全に削除される予定です。propTypesを使用している場合は、TypeScriptまたは別の型チェックのソリューションに移行することが推奨されます。

propTypesは、たとえば関数コンポーネントに渡されるpropが正しい型と値であることを確認するために使用されます。

import PropTypes from 'prop-types';

function CustomButton({ label, color }) {
    return <button style={{ backgroundColor: color }}>{ label }</button>;
}

CustomButton.defaultProps = {
    label: 'クリック',
    color: 'blue',
};

CustomButton.propTypes = {
    label: PropTypes.string,
    color: PropTypes.string,
};

今後は、TypeScriptを使用して型チェックを行うことができます。

type CustomButtonProps = {
    label?: string;
    color?: string;
};

const CustomButton = ({ label = 'クリック', color = 'blue' }: CustomButtonProps) => {
    return <button style={{ backgroundColor: color }}>{ label }</button>;
};

3. レガシーコンテキスト(contextTypesとgetChildContext)の削除

WordPressの多くのプラグインやコードベースで長年にわたって使用されてきたことから、クラスコンポーネントでcontextTypesgetChildContextといった旧式のAPIを使用しているかもしれません。これらのAPIは、各レベルで明示的にpropsを渡すことなく、親コンポーネントからその子孫にデータを渡すために使用するのが一般的でした。

見落とされがちな細かなバグにより、このレガシーコンテキストはReact 16.6.0で非推奨となり、React 19で完全に削除されます。

今後は、contextType APIを使用することになります。

WordPressプラグインで非推奨のContext APIを使用し、サイトタイトルなどのグローバル設定を親コンポーネントから子コンポーネントにprop drilling(バケツリレー)を回避して渡す例を見てみます。

import PropTypes from 'prop-types';

class SettingsProvider extends React.Component {
  static childContextTypes = {
    siteTitle: PropTypes.string.isRequired,
  };

  getChildContext() {
    return { siteTitle: '私のWordPressサイト' };
  }

  render() {
    return <SettingsConsumer />;
  }
}

class SettingsConsumer extends React.Component {
  static contextTypes = {
    siteTitle: PropTypes.string.isRequired,
  };

  render() {
    return <div>サイトタイトル: {this.context.siteTitle}</div>;
  }
}

今後はReact 19への備えとして、createContextメソッドを使用します。

import React from 'react';

const SettingsContext = React.createContext();

class SettingsProvider extends React.Component {
  render() {
    return (
      <SettingsContext value={{ siteTitle: '私のWordPressサイト' }}>
        <SettingsConsumer />
      </SettingsContext>
    );
  }
}

class SettingsConsumer extends React.Component {
  static contextType = SettingsContext;

  render() {
    const { siteTitle } = this.context;
    return <div>サイトタイトル: { siteTitle }</div>;
  }
}

4. 文字列形式refの削除

文字列形式の参照は、かつてReactコンポーネントでDOM要素にアクセスする一般的な方法でしたが、React 16.3.0で非推奨となり、React 19では削除される予定です。

文字列を使った参照は容易でしたが、名前が衝突する可能性や柔軟性の欠如など、いくつかの問題がありました。

WordPressのカスタムブロックで文字列を使った参照を行うとします。入力フィールドを含むGutenbergのカスタムブロックがあり、エディターにブロックが追加された際に入力フィールドが自動的に目立つようにしたい場合は、以下のようになります。

class CustomBlock extends React.Component {
  componentDidMount() {
    this.refs.input.focus();
  }

  render() {
    return <input ref="input" placeholder="テキストを入力してください" />;
  }
}

React 19に備えるにはコールバック形式のrefs 、またはReact.createRef APIに置き換えます。コールバック形式のrefを使用すると以下のようになります。

class CustomBlock extends React.Component {
  componentDidMount() {
    this.input.focus();
  }

  render() {
    return <input ref={(input) => (this.input = input)} placeholder="テキストを入力してください" />;
  }
}

5. モジュールパターンファクトリの削除

React 19で削除されるもう1つの非推奨機能は、モジュールパターンファクトリです。これは現在ほとんど使用されておらず、Reactが必要以上に重くなり、遅くなる原因となっていました。

モジュールパターンファクトリの使用には、慣習的でないコンポーネントを作成できるというメリットがありました。

function SettingsPanelFactory() {
  return {
    render() {
      return (
        <div className="settings-panel">
          <h2>設定</h2>
          {/* その他の設定 UIコンポーネント */}
        </div>
      );
    }
  };
}

このパターンでは、SettingsPanelFactoryはJSXを直接返すのではなく、renderメソッドを使ってオブジェクトを返します。

React 19に対応するには、このモジュールパターンファクトリを、JSXを直接返す通常の関数に移行する必要があります。

function SettingsPanel() {
  return (
    <div className="settings-panel">
      <h2> 設定</h2>
      {/* その他の設定 UIコンポーネント */}
    </div>
  );
}

6. createFactory APIの削除

React 19では、React.createFactoryも削除されます。このメソッドは、JSXが広くサポートされる前までよく使用されており、JSX構文を使わずにReact要素を作成することができました。

JSXの普及に伴い、createFactoryは徐々に旧式のメソッドとなり、よりシンプルで読みやすいJSXコードに置き換えることができるようになりました。

以下は、createFactoryを使用してbutton要素を作成する例です。button要素をユーザーの入力に基づいて動的に生成するWordPressプラグインの開発などで見られます。

import { createFactory } from 'react';

const button = createFactory('button');

function CustomButton() {
  return button({ className: 'custom-button', type: 'button' }, 'クリック');
}

このコードをReact 19に対応させるにはcreateFactoryをJSXに置き換えます。これによりコードが洗練され読みやすくなり、保守管理も簡単になります。

function CustomButton() {
  return <button className="custom-button" type="button">クリック</button>;
}

7. react-test-renderer/shallowの削除

React 19ではreact-test-renderer/shallowが削除され、これによりテストユーティリティが効率化され、ベストプラクティスが促進されます。React 18では、react-test-renderer/shallowが更新され、react-shallow-rendererが再エクスポートされました。

たとえば、react-test-renderer/shallowを使用してReactコンポーネントのshallow(浅い)レンダリングのテストを作成することができました。

import ShallowRenderer from 'react-test-renderer/shallow';

test('MyComponent shallow render', () => {
  const renderer = new ShallowRenderer();
  renderer.render(<MyComponent />);
  const result = renderer.getRenderOutput();
  expect(result.type).toBe('div');
});

React 19に対応するには、react-shallow-rendererのインストールが必要です。

npm install react-shallow-renderer --save-dev

インストール後、インポートを更新します。

import ShallowRenderer from 'react-shallow-renderer';

test('MyComponent shallow render', () => {
  const renderer = new ShallowRenderer();
  renderer.render(<MyComponent />);
  const result = renderer.getRenderOutput();
  expect(result.type).toBe('div');
});

Reactは、React Testing Libraryへの移行を推奨しています。このライブラリにより、ユーザーがコンポーネントとどのようにやり取りするかに焦点を当てることで、より堅牢なテストプラクティスが実現します。

これを行うには、@testing-library/reactライブラリを開発用の依存関係としてインストールします。

npm install @testing-library/react --save-dev

その後、同じコンポーネントを以下のようにテストすることができます。

import { render, screen } from '@testing-library/react';
import MyBlock from './MyBlock';

test('MyBlock renders correctly', () => {
  render(<MyBlock />);
  const element = screen.getByText('MyBlock content');
  expect(element).toBeInTheDocument();
});

React DOMで削除される非推奨メソッド

React 19ではReact DOMが変更され、一部の非推奨メソッドも削除されます。ここからは削除される非推奨メソッドの概要と、DOM関連のコードを更新する方法をご紹介します。

1. react-dom/test-utils APIの削除

react-dom/test-utils APIもまた、React 19で削除されます。これはReactコンポーネントのテストの書き方に影響を与え、具体的には、actユーティリティがreact-dom/test-utilsからreactパッケージに移行します。

さらに、react-dom/test-utilsの他のユーティリティもほぼすべて削除されます。以下、この変更に対応するためのテストの書き方をご紹介します。

actユーティリティは、テストに関連するすべての更新が処理され、DOMに適用されたことを確認するために欠かせません。React 19では、react-dom/test-utilsではなくreactからactを直接インポートします。

// これまで
import { act } from 'react-dom/test-utils';

// これから
import { act } from 'react';

Reactチームはまた、最新かつ十分にサポートされたテスト体験を考慮し、React Testing Libraryへの移行を推奨しています。以下、一般的な使用例とその更新方法をご紹介します。

renderIntoDocumentユーティリティは削除され、@testing-library/reactからrenderに置き換えられます。

// これまで
import { renderIntoDocument } from 'react-dom/test-utils';

renderIntoDocument(<Component />);

// これから
import { render } from '@testing-library/react';

render(<Component />);

同様に、イベントを再現するためのSimulateユーティリティも削除されます。これには、要素に実際のイベントを発生させる@testing-library/reactfireEventを使用します。

// これまで
import { Simulate } from 'react-dom/test-utils';

const element = document.querySelector('button');
Simulate.click(element);

// これから
import { fireEvent } from '@testing-library/react';

const element = document.querySelector('button');
fireEvent.click(element);

なお、fireEventは実際のイベントを発生させるため、Simulateによって作成された合成イベントよりも自然に要素と相互作用します。React Testing Libraryの詳細はこちらをご覧ください。

2. findDOMNode APIの削除

React 16.6.0で非推奨となったReactDOM.findDOMNodeの削除もまた、React 19の重要な変更点です。

この関数は、Reactコンポーネントの基礎となるDOMノードにアクセスするために使用されていましたが、実行速度が遅い、リファクタリングが困難、抽象化レイヤーを破壊するなど、いくつかの欠点がありました。

今後は、ReactコンポーネントのDOM要素との対話により信頼性が高く効率的な方法を提供するDOM用のrefsを使用します。

以下は、コンポーネントがマウントされた際に入力フィールドのテキストを選択するためにfindDOMNodeを使用する例です。

import { findDOMNode } from 'react-dom';

function AutoselectingInput() {
  useEffect(() => {
    const input = findDOMNode(this);
    input.select()
  }, []);

  render() {
    return <input defaultValue="Hello" />;
  }
}

このコードをReact 19に対応させるには、findDOMNoderefに置き換えます。これによりコードがより堅牢になり、最新のReactベストプラクティスに沿うようになります。

import React, { useEffect, useRef } from 'react';

function AutoselectingInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.select();
  }, []);

  return <input ref={inputRef} defaultValue="Hello" />;
}

3. ReactDOM.renderの削除

ReactDOM.renderも削除されます。このメソッドはReact 18.0.0で非推奨となり、Reactアプリの初期化・レンダリングにより効率的で新しい方法を提供するreact-dom/clientからのcreateRoot APIが代わりに採用されました。この変更は、ライブラリの合理化と最適化を目指すReactの継続的な取り組みの一環です。

一般的なWordPressの設定では、DOMの用意ができた際にReactアプリを初期化するカスタムブロックやプラグインがあります。以前は、このような状況でReactDOM.renderが使われていました。

import { render } from 'react-dom';
render(<App />, document.getElementById('root'));

React 19では、Reactアプリケーションの初期化とレンダリングにcreateRootを使用します。

import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);

4. unmountComponentAtNode APIの削除

React 18.0.0で非推奨となったReactDOM.unmountComponentAtNodeも削除されます。このメソッドは、ReactコンポーネントをDOMからアンマウントするために使用されていました。

React 19 では、ルーツの作成とハイドレーションのAPIの更新により、代わりにroot.unmount()メソッドを使用します。

// これまで
unmountComponentAtNode(document.getElementById('root'));

// これから
root.unmount();

5. ReactDOM.hydrateの削除

ReactDOM.hydrateはReact 18で非推奨となり、React 19で完全に削除される予定です。

ReactDOM.hydrateを置き換えるのは、クライアント用のReact DOM APIの新たなメソッドであるhydrateRootです。サーバーでレンダリングされたReactアプリケーションをハイドレートする、効率的かつ最新の手法となります。

たとえばWordPressの文脈では、ページの読み込みを高速化するため、サーバーサイドレンダリング(SSR)を使用して最初のHTMLコンテンツを配信することがあります。このコンテンツをインタラクティブなReactアプリケーションにハイドレートするには、ReactDOM.hydrateを使用するのが一般的でした。

import { hydrate } from 'react-dom';
import App from './App.js';

hydrate(
  <App />,
  document.getElementById('root')
);

React 19では、ハイドレーションにreact-dom/clienthydrateRootを使用します。

import { hydrateRoot } from 'react-dom/client';
import App from './App.js';

hydrateRoot(
  document.getElementById('root'),
  <App />
);

削除される非推奨のTypeScript型

WordPress開発では、Reactコンポーネントの型安全性、コードの質を向上させるためにTypeScriptを一般的に使用します。React 19では、非推奨TypeScript型の一部が削除、またはより関連性の高いパッケージに移行されます。

この変更を把握することは、コードベースとReactの最新バージョンの互換性を確保し、堅牢なコードを維持するために非常に重要になります。

Reactチームは、移行を支援するtypes-react-codemodツールを提供しています。

これを使用するには、以下のcodemodコマンドを実行してください。これには非推奨の型を更新するためのコード変換が含まれています。

npx types-react-codemod@latest preset-19 ./path-to-app

またこのツールには対話モードもあり、特定の変換を選んで適用可能です。

? Pick transforms to apply (Press  to select,  to toggle all,  to invert selection, and  to proceed)
❯◯ context-any
◉ deprecated-react-type
◉ deprecated-sfc-element
◉ deprecated-sfc
◉ deprecated-stateless-component
◯ implicit-children
◯ useCallback-implicit-any

以下、主な変更点の例を見てみましょう。

1. refクリーンアップの必須化

React 19では、refのクリーンアップ関数がコールバック形式のrefで明示的なリターンを強制することで、型安全性を確保しています。暗黙の戻り値は、TypeScriptが戻り値を誤解釈する原因になり得ます。

// これまで
 (instance = current)} />

// これから
 { instance = current }} />

2. useRefの引数の必須化

以前は、引数なしでuseRefを呼び出すことができたため、潜在的な型の問題を引き起こしていました。React 19では、useRefはrefが常にミュータブルであることを保証するために引数を必要とします。

// これまで — @ts-expect-error: 1つの引数が期待されていたが何も見つからなかった
useRef();

// これから — 引数を使った正しい使用方法
useRef(undefined);

3. ReactElement TypeScript型の変更

ReactElement propsのデフォルトの型がanyからunknownに変更され、未知の型の明示的な処理を要求することで、型の安全性が向上します。

// これまで ─ 'any'
type Example = ReactElement["props"];

// これから  ─ 'unknown'
type Example = ReactElement["props"];

anyに依存していたコードは、unknownを明示的に処理するように更新するか、anyにキャストする必要があります。

まとめ

WordPress開発を行う場合、最新のReactに対応することは非常に重要です。今回は、Reactのさまざまな変更点をご紹介し、WordPressプロジェクトに適用する方法をご紹介しました。

React 19では、新たなJSXトランスフォームが必要になりますが、幸いWordPress 6.6に標準搭載されます。このトランスフォームが有効になっていない場合は、以下のような警告が表示されます。

Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform

アプリケーション(またはその依存関係の1つ)が古いJSXトランスフォームを使用しています。最新のものに更新してパフォーマンスを向上させてください

なお、JSXトランスフォームにReactインポートを使用する必要はありません。

React 19への対応に関して紹介が漏れている点やご質問などがありましたら、以下のコメント欄でお知らせください。

Joel Olawanle Kinsta

Kinstaでテクニカルエディターとして働くフロントエンド開発者。オープンソースをこよなく愛する講師でもあり、JavaScriptとそのフレームワークを中心に200件以上の技術記事を執筆している。