Metaprogramação com React

Mar 09 2023
O objetivo não é entrar em detalhes sobre o que é metaprogramação e suas técnicas, mas apenas ver um exemplo de como aplicá-la no React. Este é meu primeiro post neste meio fantástico, e gostaria de escrever um pouco sobre uma técnica que já usamos uma vez na vida (de um programador), mas que talvez não conheçamos em detalhes, a metaprogramação! O código abaixo é um exemplo de como usar a construção Proxy e a API Reflect, dentro de um componente React.
Foto de Lautaro Andreani no Unsplash

O objetivo não é entrar em detalhes sobre o que é metaprogramação e suas técnicas, mas apenas ver um exemplo de como aplicá-la no React.

Este é meu primeiro post neste meio fantástico, e gostaria de escrever um pouco sobre uma técnica que já usamos uma vez na vida (de um programador), mas que talvez não conheçamos em detalhes, a metaprogramação !

O código abaixo é um exemplo de como usar a construção Proxy e a API Reflect , dentro de um componente React. O componente MyComponent usa o estado local do React ( useState ) para gerenciar um objeto proxyData que tem uma propriedade “ test ” inicialmente vazia. O componente possui três botões que, ao serem clicados, modificam a propriedade “teste” do objeto 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 />;
}

O primeiro botão, handleClickMemo , usa a função React.useCallback para criar uma função que é armazenada em cache pelo React. Isso é útil para evitar que a função seja reconstruída toda vez que o componente for atualizado. A função cria um novo objeto Proxy com base no objeto proxyData , atualiza a propriedade “test” com um novo valor gerado aleatoriamente e define o estado de proxyData com o objeto recém-criado.

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 observar que o uso de um proxy pode ter efeitos colaterais e causar problemas de desempenho se não for usado corretamente. Além disso, a atualização de estados por meio de um objeto Proxy pode não ser detectada pelo React, portanto, deve-se ter cuidado ao usar Proxies em um componente React.

Neste link , você pode ver no console o comportamento de cada botão.