Metaprogrammazione con React

Mar 09 2023
Lo scopo non è entrare nei dettagli su cosa sia la metaprogrammazione e le sue tecniche, ma solo vedere un esempio di come applicarla in React. Questo è il mio primo post su questo fantastico mezzo, e vorrei scrivere un po' su una tecnica che abbiamo già usato una volta nella vita (da un programmatore), ma che forse non conosciamo in dettaglio, la metaprogrammazione! Il codice seguente è un esempio di come utilizzare il costrutto Proxy e l'API Reflect, all'interno di un componente React.
Foto di Lautaro Andreani su Unsplash

Lo scopo non è entrare nei dettagli su cosa sia la metaprogrammazione e le sue tecniche, ma solo vedere un esempio di come applicarla in React.

Questo è il mio primo post su questo fantastico mezzo, e vorrei scrivere un po' su una tecnica che abbiamo già usato una volta nella vita (da un programmatore), ma che forse non conosciamo in dettaglio, la metaprogrammazione !

Il codice seguente è un esempio di come utilizzare il costrutto Proxy e l'API Reflect , all'interno di un componente React. Il componente MyComponent utilizza lo stato locale di React ( useState ) per gestire un oggetto proxyData che ha una proprietà " test " inizialmente vuota. Il componente dispone di tre pulsanti che, se cliccati, modificano la proprietà “test” dell'oggetto proxyData .

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 />;
}

Il primo pulsante, handleClickMemo , utilizza la funzione React.useCallback per creare una funzione memorizzata nella cache da React. Questo è utile per evitare che la funzione venga ricostruita ogni volta che il componente viene aggiornato. La funzione crea un nuovo oggetto Proxy basato sull'oggetto proxyData , aggiorna la proprietà "test" con un nuovo valore generato casualmente e imposta lo stato di proxyData con l'oggetto appena creato.

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);
  };

È importante notare che l'utilizzo di un proxy può avere effetti collaterali e può causare problemi di prestazioni se non utilizzato correttamente. Inoltre, l'aggiornamento degli stati tramite un oggetto Proxy potrebbe non essere rilevato da React, quindi è necessario prestare attenzione quando si utilizzano i proxy all'interno di un componente React.

In questo link , puoi vedere nella console il comportamento di ciascun pulsante.