React によるメタプログラミング

Mar 09 2023
この目的は、メタプログラミングとそのテクニックについて詳しく説明することではなく、メタプログラミングを React に適用する方法の例を確認することだけです。これは、この素晴らしいメディアへの私の最初の投稿です。私たちが人生で一度は使ったことがある (プログラマーから) ものの、詳しくは知らないかもしれないテクニックの 1 つであるメタプログラミングについて少し書きたいと思います。以下のコードは、React コンポーネント内で Proxy コンストラクトと Reflect API を使用する方法の例です。
Unsplash の Lautaro Andreani による写真

この目的は、メタプログラミングとそのテクニックについて詳しく説明することではなく、メタプログラミングを React に適用する方法の例を確認することだけです。

これは、この素晴らしいメディアへの私の最初の投稿です。私たちが人生で一度は使用したことがある (プログラマーから) ものの、詳しくは知らないかもしれない 1 つのテクニック、メタプログラミングについて少し書きたいと思います。

以下のコードは、React コンポーネント内でProxy コンストラクトとReflect APIを使用する方法の例です。 MyComponentコンポーネントは、React のローカル状態 ( useState )を使用して、最初は空の「 test 」プロパティを持つproxyDataオブジェクトを管理します。このコンポーネントには 3 つのボタンがあり、クリックするとproxyDataオブジェクトの「test」プロパティを変更します。

import React, { useState } from 'react';

function MyComponent() {
  const [proxyData, setProxyData] = useState({ test: '' });
  const [render, setRender] = useState(false);

  const handleClickMemo = React.useCallback(() => {
    const updatedData = new Proxy(proxyData, {
      set(target, prop, value) {
        const success = Reflect.set(target, prop, value);
        if (success) {
          console.log(`Prop ${prop} modified with value ${value}`);
        }
        return success;
      },
    });
    updatedData.test = crypto.randomUUID();
    setProxyData(updatedData);
  }, []);

  // without useCallback, do new proxy every time
  const handleClick = () => {
    const updatedData = new Proxy(proxyData, {
      set(target, prop, value) {
        const success = Reflect.set(target, prop, value);
        if (success) {
          console.warn(`Prop ${prop} modified with value ${value}`);
        }
        return success;
      },
    });
    updatedData.test = crypto.randomUUID();
    setProxyData(updatedData);
  };

  // case where react doesn't render, but in console,
  // if it is a proxy the object is updated
  // BE CAREFUL when using proxies
  const handleClickNoRender = () => {
    proxyData.test = crypto.randomUUID();
    setProxyData(proxyData);
  };

  return (
    <div>
      <pre>{JSON.stringify(proxyData)}</pre>
      <button onClick={handleClickMemo}>handleClickMemo</button>
      <button onClick={handleClick}>handleClick</button>
      <button onClick={handleClickNoRender}>handleClickNoRender</button>
      <br />
      <button onClick={() => setRender(!render)}>re-render</button>
    </div>
  );
}

export default function App() {
  return <MyComponent />;
}

最初のボタンhandleClickMemo は、React.useCallback関数を使用して、React によってキャッシュされる関数を作成します。これは、コンポーネントが更新されるたびに関数が再構築されるのを避けるのに役立ちます。この関数は、proxyDataオブジェクトに基づいて新しいProxyオブジェクトを作成し、ランダムに生成された新しい値で「test」プロパティを更新し、新しく作成されたオブジェクトでproxyDataの状態を設定します。

const handleClickMemo = React.useCallback(() => {
    const updatedData = new Proxy(proxyData, {
      set(target, prop, value) {
        const success = Reflect.set(target, prop, value);
        if (success) {
          console.log(`Prop ${prop} modified with value ${value}`);
        }
        return success;
      },
    });
    updatedData.test = crypto.randomUUID();
    setProxyData(updatedData);
  }, []);

// without useCallback, do new proxy every time
  const handleClick = () => {
    const updatedData = new Proxy(proxyData, {
      set(target, prop, value) {
        const success = Reflect.set(target, prop, value);
        if (success) {
          console.warn(`Prop ${prop} modified with value ${value}`);
        }
        return success;
      },
    });
    updatedData.test = crypto.randomUUID();
    setProxyData(updatedData);
  };

// case where react doesn't render, but in console,
  // if it is a proxy the object is updated
  // BE CAREFUL when using proxies
  const handleClickNoRender = () => {
    proxyData.test = crypto.randomUUID();
    setProxyData(proxyData);
  };

プロキシ を使用すると副作用が発生する可能性があり、正しく使用しないとパフォーマンスの問題が発生する可能性があることに注意することが重要です。また、Proxyオブジェクトを介した状態の更新は React によって検出されない可能性があるため、React コンポーネント内で Prox を使用する場合は注意が必要です。

このリンクでは、コンソールで各ボタンの動作を確認できます。