<h2>Entendendo a Arquitetura do React</h2>
<p>React é uma biblioteca que revolucionou a forma como construímos interfaces. Sua beleza reside na simplicidade do modelo mental: componentes são funções que retornam descrições de UI, e React gerencia a atualização eficiente do DOM. Para compreender verdadeiramente como React funciona, vamos construir uma versão simplificada que captura os conceitos essenciais.</p>
<p>A arquitetura do React se baseia em três pilares fundamentais: a representação declarativa da UI através de elementos (criados com <code>createElement</code>), a transformação dessa representação em DOM real (através de <code>render</code>), e a capacidade de componentes manterem estado e reagirem a mudanças (através de hooks). Entender esses pilares é crucial porque quando você domina os mecanismos internos, escreve código React mais eficaz e consegue debugar problemas muito mais rapidamente.</p>
<h3>O que é um Elemento React?</h3>
<p>Um elemento React não é um componente — é a representação mais básica de algo que você quer renderizar. Pense em um elemento como um objeto JavaScript comum que descreve: "Eu quero um botão, com essas props, com esses filhos". Isso é tudo. Não há magia aqui. Um elemento é apenas dados, uma estrutura que diz ao React o que renderizar.</p>
<pre><code class="language-javascript">// Um elemento é simplesmente um objeto descritivo
const elemento = {
type: 'button',
props: {
className: 'btn-primary',
onClick: () => alert('Clicado!'),
children: ['Clique aqui']
}
};</code></pre>
<h2>Implementando createElement</h2>
<p><code>createElement</code> é a função que você chama (ou que o JSX transpila para chamar) para criar esses objetos que descrevem a UI. Quando você escreve <code><Button /></code>, o transpilador converte isso em <code>createElement(Button, null)</code>. É um passo crucial compreender que JSX é apenas açúcar sintático.</p>
<h3>A Função createElement</h3>
<p>A função <code>createElement</code> recebe três argumentos principais: o tipo (pode ser uma string como <code>'div'</code> ou uma função de componente), as props (propriedades e atributos), e os filhos (children). Ela retorna um objeto simples que descreve o elemento.</p>
<pre><code class="language-javascript">function createElement(type, props, ...children) {
// Se não há props, inicializa um objeto vazio
const elementProps = props || {};
// Os children precisam ser normalizados: arrays aninhados viram um array único
const flatChildren = children.flat();
// Se há filhos, eles são adicionados às props
if (flatChildren.length > 0) {
elementProps.children = flatChildren.length === 1
? flatChildren[0]
: flatChildren;
}
// Retorna o objeto que descreve o elemento
return {
type,
props: elementProps
};
}
// Exemplos de uso:
const elemento1 = createElement('div', { className: 'container' },
createElement('h1', null, 'Olá Mundo')
);
const elemento2 = createElement('button', { onClick: () => {} }, 'Enviar');
console.log(elemento1);
// Output: {
// type: 'div',
// props: {
// className: 'container',
// children: { type: 'h1', props: { children: 'Olá Mundo' } }
// }
// }</code></pre>
<h3>Lidando com Componentes Funcionais</h3>
<p>Componentes funcionais são diferentes de elementos primitivos. Um componente é uma função que retorna um elemento. Quando <code>createElement</code> recebe uma função (ao invés de uma string), ele não deve criar um objeto imediatamente — esse trabalho será feito durante a renderização.</p>
<pre><code class="language-javascript">function Botao({ label, onClick }) {
return createElement('button', { onClick, className: 'btn' }, label);
}
// Quando chamamos createElement com um componente:
const meuBotao = createElement(Botao, { label: 'Clique', onClick: () => {} });
console.log(meuBotao);
// Output: {
// type: Botao, // A função em si, não o resultado
// props: { label: 'Clique', onClick: ... }
// }</code></pre>
<h2>Implementando Render</h2>
<p><code>render</code> é onde a mágica acontece: ele pega esses objetos descritivos (elementos) e os transforma em DOM real, inserindo-os na página. Este é também o ponto onde precisamos lidar com atualizações eficientes — React não reconstrói tudo do zero, ele difere o novo estado do antigo e aplica apenas as mudanças necessárias.</p>
<h3>A Função Render Básica</h3>
<p>Começamos com uma implementação simples que apenas cria o DOM. Depois evoluiremos para lidar com atualizações e reconciliação.</p>
<pre><code class="language-javascript">function render(elemento, container) {
// Se o elemento é texto ou número, cria um nó de texto
if (typeof elemento === 'string' || typeof elemento === 'number') {
container.appendChild(document.createTextNode(elemento));
return;
}
// Se é um array, renderiza cada item
if (Array.isArray(elemento)) {
elemento.forEach(el => render(el, container));
return;
}
// Se é null ou undefined, não faz nada
if (!elemento) {
return;
}
const { type, props } = elemento;
// Se type é uma string, é um elemento HTML primitivo
if (typeof type === 'string') {
// Cria o elemento DOM
const domElement = document.createElement(type);
// Aplica as props (atributos, listeners, etc)
Object.entries(props).forEach(([key, value]) => {
if (key === 'children') {
// Children precisam ser renderizados recursivamente
if (value) {
render(value, domElement);
}
} else if (key.startsWith('on')) {
// Listeners de eventos (onClick, onChange, etc)
const eventName = key.substring(2).toLowerCase();
domElement.addEventListener(eventName, value);
} else if (key !== 'key') {
// Atributos normais
domElement.setAttribute(key, value);
}
});
container.appendChild(domElement);
} else if (typeof type === 'function') {
// Se type é uma função, é um componente
// Chama a função com as props para obter o elemento que ela retorna
const componentElement = type(props);
// Renderiza o elemento retornado
render(componentElement, container);
}
}
// Exemplo de uso:
const app = createElement('div', { className: 'app' },
createElement('h1', null, 'Meu Mini React'),
createElement('p', null, 'Isso é incrível!')
);
render(app, document.getElementById('root'));</code></pre>
<h3>Renderização com Componentes e Estado</h3>
<p>A coisa fica mais interessante quando queremos que componentes mantenham estado. Para isso, precisamos rastrear qual componente está sendo renderizado no momento. Esta é a base dos hooks do React.</p>
<pre><code class="language-javascript">// Rastreadores globais para hooks
let currentComponent = null;
let componentHooks = new Map();
let hookIndex = 0;
function render(elemento, container) {
if (typeof elemento === 'string' || typeof elemento === 'number') {
container.appendChild(document.createTextNode(elemento));
return;
}
if (Array.isArray(elemento)) {
elemento.forEach(el => render(el, container));
return;
}
if (!elemento) {
return;
}
const { type, props } = elemento;
if (typeof type === 'string') {
const domElement = document.createElement(type);
Object.entries(props).forEach(([key, value]) => {
if (key === 'children') {
if (value) {
render(value, domElement);
}
} else if (key.startsWith('on')) {
const eventName = key.substring(2).toLowerCase();
domElement.addEventListener(eventName, value);
} else if (key !== 'key') {
domElement.setAttribute(key, value);
}
});
container.appendChild(domElement);
} else if (typeof type === 'function') {
// Antes de chamar o componente, definimos qual é o componente atual
currentComponent = type;
hookIndex = 0;
// Se não há hooks registrados para este componente, cria um array vazio
if (!componentHooks.has(type)) {
componentHooks.set(type, []);
}
const componentElement = type(props);
render(componentElement, container);
}
}</code></pre>
<h2>Implementando Hooks (useState e useEffect)</h2>
<p>Hooks são funções que permitem componentes funcionais acessarem recursos que antes eram exclusivos de componentes de classe, como estado e efeitos colaterais. <code>useState</code> é o hook mais fundamental — ele permite que um componente tenha estado. <code>useEffect</code> permite executar código em resposta a mudanças.</p>
<h3>useState: Adicionando Estado a Componentes Funcionais</h3>
<p><code>useState</code> retorna um array com dois elementos: o valor atual do estado e uma função para atualizá-lo. Cada componente possui seu próprio conjunto de estados, e precisamos rastreá-los por índice (é por isso que a ordem dos hooks importa).</p>
<pre><code class="language-javascript">function useState(initialValue) {
// Obtém os hooks do componente atual
const hooks = componentHooks.get(currentComponent);
// O índice do hook atual (useState, outro useState, useEffect, etc)
const index = hookIndex;
hookIndex++;
// Se este hook não foi inicializado ainda, inicializa
if (!hooks[index]) {
hooks[index] = {
value: typeof initialValue === 'function'
? initialValue()
: initialValue,
setState: null // Será definido abaixo
};
}
const hook = hooks[index];
// Define a função setState que atualiza o estado
hook.setState = (newValue) => {
const actualNewValue = typeof newValue === 'function'
? newValue(hook.value)
: newValue;
// Se o valor não mudou, não faz nada
if (actualNewValue === hook.value) {
return;
}
// Atualiza o valor
hook.value = actualNewValue;
// Reinicializa o índice de hooks
hookIndex = 0;
// Renderiza novamente o componente
const currentHooks = componentHooks.get(currentComponent);
const componentElement = currentComponent({});
// Encontra o container anterior e limpa
const container = document.getElementById('root');
container.innerHTML = '';
// Renderiza novamente
render(componentElement, container);
};
return [hook.value, hook.setState];
}
// Exemplo prático:
function Contador() {
const [count, setCount] = useState(0);
return createElement('div', { className: 'contador' },
createElement('p', null, Contador: ${count}),
createElement('button', {
onClick: () => setCount(count + 1)
}, 'Incrementar')
);
}
// Uso:
render(createElement(Contador, {}), document.getElementById('root'));</code></pre>
<h3>useEffect: Efeitos Colaterais e Cleanup</h3>
<p><code>useEffect</code> permite executar código após o componente ser renderizado. É útil para requisições, subscrições, e limpeza. Ele aceita uma função e um array de dependências — o efeito é executado quando qualquer dependência muda (ou em toda renderização, se não houver dependências).</p>
<pre><code class="language-javascript">function useEffect(callback, dependencies) {
const hooks = componentHooks.get(currentComponent);
const index = hookIndex;
hookIndex++;
// Inicializa o hook se necessário
if (!hooks[index]) {
hooks[index] = {
cleanup: null,
dependencies: null
};
}
const hook = hooks[index];
const hasNoDependencies = !dependencies;
const dependenciesChanged = !hook.dependencies ||
dependencies.some((dep, i) => dep !== hook.dependencies[i]);
// Executa o efeito se não há dependências ou se elas mudaram
if (hasNoDependencies || dependenciesChanged) {
// Limpa o efeito anterior, se existir
if (hook.cleanup) {
hook.cleanup();
}
// Executa o novo efeito
const cleanup = callback();
hook.cleanup = typeof cleanup === 'function' ? cleanup : null;
hook.dependencies = dependencies;
}
}
// Exemplo: fetch de dados
function Usuario({ userId }) {
const [usuario, setUsuario] = useState(null);
const [carregando, setCarregando] = useState(true);
useEffect(() => {
// Simula uma requisição
setCarregando(true);
setTimeout(() => {
setUsuario({ id: userId, nome: 'João Silva' });
setCarregando(false);
}, 500);
// Cleanup (opcional)
return () => {
console.log('Componente foi desmontado ou dependências mudaram');
};
}, [userId]); // Reexecuta quando userId muda
if (carregando) {
return createElement('p', null, 'Carregando...');
}
return createElement('div', null,
createElement('h2', null, usuario.nome),
createElement('p', null, ID: ${usuario.id})
);
}</code></pre>
<h2>Colocando Tudo Junto: Um Exemplo Completo</h2>
<p>Agora vamos integrar tudo em um exemplo funcional que demonstra createElement, render e hooks trabalhando juntos.</p>
<pre><code class="language-javascript">// Sistema de hooks
let currentComponent = null;
let componentHooks = new Map();
let hookIndex = 0;
function useState(initialValue) {
const hooks = componentHooks.get(currentComponent);
const index = hookIndex;
hookIndex++;
if (!hooks[index]) {
hooks[index] = {
value: typeof initialValue === 'function' ? initialValue() : initialValue,
setState: null
};
}
const hook = hooks[index];
hook.setState = (newValue) => {
const actualNewValue = typeof newValue === 'function'
? newValue(hook.value)
: newValue;
if (actualNewValue === hook.value) return;
hook.value = actualNewValue;
hookIndex = 0;
const container = document.getElementById('root');
container.innerHTML = '';
const componentElement = currentComponent({});
render(componentElement, container);
};
return [hook.value, hook.setState];
}
function useEffect(callback, dependencies) {
const hooks = componentHooks.get(currentComponent);
const index = hookIndex;
hookIndex++;
if (!hooks[index]) {
hooks[index] = { cleanup: null, dependencies: null };
}
const hook = hooks[index];
const hasNoDependencies = !dependencies;
const dependenciesChanged = !hook.dependencies ||
dependencies.some((dep, i) => dep !== hook.dependencies[i]);
if (hasNoDependencies || dependenciesChanged) {
if (hook.cleanup) hook.cleanup();
const cleanup = callback();
hook.cleanup = typeof cleanup === 'function' ? cleanup : null;
hook.dependencies = dependencies;
}
}
// Core functions
function createElement(type, props, ...children) {
const elementProps = props || {};
const flatChildren = children.flat();
if (flatChildren.length > 0) {
elementProps.children = flatChildren.length === 1
? flatChildren[0]
: flatChildren;
}
return { type, props: elementProps };
}
function render(elemento, container) {
if (typeof elemento === 'string' || typeof elemento === 'number') {
container.appendChild(document.createTextNode(elemento));
return;
}
if (Array.isArray(elemento)) {
elemento.forEach(el => render(el, container));
return;
}
if (!elemento) return;
const { type, props } = elemento;
if (typeof type === 'string') {
const domElement = document.createElement(type);
Object.entries(props).forEach(([key, value]) => {
if (key === 'children') {
if (value) render(value, domElement);
} else if (key.startsWith('on')) {
const eventName = key.substring(2).toLowerCase();
domElement.addEventListener(eventName, value);
} else if (key !== 'key') {
domElement.setAttribute(key, value);
}
});
container.appendChild(domElement);
} else if (typeof type === 'function') {
currentComponent = type;
hookIndex = 0;
if (!componentHooks.has(type)) {
componentHooks.set(type, []);
}
const componentElement = type(props);
render(componentElement, container);
}
}
// Componentes
function App() {
const [mensagem, setMensagem] = useState('Bem-vindo ao Mini React!');
const [cliques, setCliques] = useState(0);
useEffect(() => {
console.log(Cliques: ${cliques});
}, [cliques]);
return createElement('div', { style: 'text-align: center; padding: 20px;' },
createElement('h1', null, mensagem),
createElement('p', null, Você clicou ${cliques} vezes),
createElement('button', {
onClick: () => setCliques(cliques + 1),
style: 'padding: 10px 20px; font-size: 16px; cursor: pointer;'
}, 'Clique aqui')
);
}
// Inicializa
render(createElement(App, {}), document.getElementById('root'));</code></pre>
<p>HTML para testar:</p>
<pre><code class="language-html"><!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Mini React</title>
</head>
<body>
<div id="root"></div>
<script src="mini-react.js"></script>
</body>
</html></code></pre>
<h2>Conclusão</h2>
<p>Implementar um mini React do zero ensina três lições fundamentais que transformam sua compreensão sobre a biblioteca. <strong>Primeiro</strong>, elementos são apenas objetos JavaScript — não há magia, apenas descrições de UI que o React interpreta. Compreender que JSX é açúcar sintático para <code>createElement</code> remove muito da "mágica negra" que envolta React. <strong>Segundo</strong>, hooks funcionam através de rastreamento de índice — a ordem importa porque React usa a posição do hook na renderização para identificá-lo, não nomes. Isso explica por que não podemos usar hooks condicionalmente. <strong>Terceiro</strong>, renderização é recursiva e componentes são funções — quando você entende que um componente é simplesmente uma função que retorna um elemento, e que esse elemento é renderizado recursivamente, a maioria dos comportamentos estranhos do React fazem sentido perfeito.</p>
<p>Este exercício também revela limitações importantes da nossa implementação (sem virtual DOM, sem diffing eficiente, sem limpeza de memória), que nos leva a apreciar a engenharia elegante que a equipe do React fez. Use esse conhecimento não apenas para entender React melhor, mas para fazer decisões melhores ao construir componentes.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://react.dev/reference/react/createElement" target="_blank" rel="noopener noreferrer">React Official Documentation - Creating Elements</a></li>
<li><a href="https://react.dev/reference/react/hooks" target="_blank" rel="noopener noreferrer">React Hooks Documentation</a></li>
<li><a href="https://egghead.io/courses/the-beginner-s-guide-to-react" target="_blank" rel="noopener noreferrer">Building React from Scratch by Kent C. Dodds</a></li>
<li><a href="https://javascript.info/generators" target="_blank" rel="noopener noreferrer">JavaScript.info - Generators and Iterables</a></li>
<li><a href="https://overreacted.io/" target="_blank" rel="noopener noreferrer">A Deep Dive into React Hooks by Dan Abramov</a></li>
</ul>
<p><!-- FIM --></p>