Estágio 02 · 02-04
LockedA maior parte dos devs React opera num modelo mental simplificado: "componente é função, hooks são state". Funciona pra fazer features triviais. Quebra na hora de:
useMemo e useState.key é crítica (e por que index como key dá bug sutil).A diferença entre Pleno e Senior em React é profundidade do modelo: como o reconciler decide o que re-renderizar, o que Fiber permite, o que mudou com Concurrent Mode, o que RSC realmente é. Sem isso, você é refém do framework.
A premissa do React: você descreve o que deve aparecer dado um estado, não como chegar lá. Quando o estado muda, React reconcilia (descobre o diff) e atualiza o DOM minimamente.
Esse é um dos paradigmas mais importantes do frontend moderno, e tem custo. React precisa comparar estado anterior vs novo pra calcular o diff. Esse processo é a "reconciliação", e é onde a maior parte das otimizações vive.
"Virtual DOM" é o nome popular pra: representação em memória da árvore de UI. Em React, isso são objetos JS chamados elementos (criados via JSX → React.createElement → objetos { type, props, key, ref }).
Em cada render, sua função componente retorna uma nova árvore de elements. React compara com a árvore anterior, calcula o diff, e aplica ao DOM real (ou a outro renderer, React Native, Ink, etc.).
A palavra "virtual" sugere mágica que não existe. É só representação intermediária.
Fiber é a estrutura interna que React mantém pra cada nó da árvore. Cada Fiber contém:
type, key, props, statechild, sibling, return (parent), linked treealternate, versão "atual" vs "work-in-progress"A linked tree em vez de tree-recursivo permite interrupção e retomada: React pode pausar a meio de um render se algo mais prioritário chega (input do usuário, p.ex.), e voltar depois. Isso é a base do Concurrent Mode.
Fases do trabalho:
Por isso effects rodam depois do paint, e por isso useState setter durante render é problemático (você está dentro da render phase, mexendo em state que React ainda não terminou de processar).
Algoritmo de diff é heurístico, O(n) (vs O(n³) ótimo). Premissas:
key pra identificar mesma "entidade lógica" entre renders.Sem key (ou com index como key) em lista mutável, React assume "mesmo elemento na mesma posição", bug clássico: você reordena, mas state interno (input value, focus) acompanha o índice, não o item.
Use key estável e única do domínio (order.id, não index). Index como key só está OK em listas estáticas que nunca reordenam.
Um componente re-renderiza quando:
setX).useState, useReducer triggeram. setState(v) agenda render. Múltiplos setState dentro do mesmo handler/effect são batched (um único render).
React.memo(Component) pula re-render se props (shallow comparison) não mudaram. Atalho que parece grátis mas pode não ser:
Hooks dependem de ordem de chamada estável. Internamente, React mantém uma linked list de hooks por componente. Cada useState é uma "slot" na lista. Por isso:
useState:
useState(() => expensive()), função roda só no mount.setX(prev => prev + 1), evita stale closures.useEffect:
[] é "uma vez no mount", [a, b] é "quando a ou b mudam".useLayoutEffect:
useEffect (não bloqueia paint).useMemo, useCallback:
useMemo(() => compute(), [deps]), pra cálculo caro ou referência estável (passar pra React.memo filho).useCallback(fn, [deps]), açúcar pra useMemo(() => fn, [deps]).useRef:
ref.current é reescrevível sem re-render.<div ref={ref}>), valor mutável que não dispara render (timers, last value, cancellation tokens).useReducer:
useContext:
useTransition, useDeferredValue (Concurrent):
useSyncExternalStore, para integrar stores externos (Redux, Zustand) com Concurrent Mode corretamente.
React Compiler (RC 2024, GA 2025-2026) automatiza memoization estática. Vale entender o modelo mental novo porque muda profundamente como você escreve componentes.
O que ele faz:
useMemo chains).Antes do compiler:
const Card = memo(function Card({ user, onSelect }) {
const fullName = useMemo(() => `${user.first} ${user.last}`, [user.first, user.last]);
const handleClick = useCallback(() => onSelect(user.id), [onSelect, user.id]);
return <button onClick={handleClick}>{fullName}</button>;
});
Com compiler:
function Card({ user, onSelect }) {
const fullName = `${user.first} ${user.last}`;
return <button onClick={() => onSelect(user.id)}>{fullName}</button>;
}
Compiler detecta que fullName depende só de user.first/.last, que onClick depende de onSelect/user.id, e gera memoization equivalente. Sem React.memo, sem useMemo, sem useCallback. O código fica como você escreveria sem otimização, performance vem grátis.
Rules of React (compiler-friendly):
eslint-plugin-react-compiler (build-time) avisa quando código viola e o compiler vai pular esse arquivo (bail-out).
Bail-out behavior:
Migrações práticas:
useMemo em massa. Compiler é resiliente, manter useMemos antigos não quebra.React.memo redundante depois de auditar, compiler memoiza componentes shallow-equal-prop automaticamente.react-compiler/cannot-be-compiled flagsa razão exata da bail-out.Quando ainda escrever useMemo manualmente:
O que muda em interview/review:
useMemo/useCallback em código novo é sinal de "não confia no compiler", questione, não copie.Suspense é mecanismo pra componentes esperarem algo (data, código lazy-loaded) sem você precisar gerenciar loading state manualmente.
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
Quando algo dentro de Comments "throw a Promise" (convenção), React intercepta, mostra fallback, e re-tenta render quando promise resolve.
Use cases:
lazy() pra code splitting: const Comments = lazy(() => import('./Comments')).React Query com suspense: true, RSC, Relay).Streaming SSR em React 18+: o servidor manda HTML em pedaços conforme suspense boundaries resolvem. Cliente hydrata progressivamente. UX melhor (TTFB rápido, conteúdo não-crítico depois).
RSC é uma mudança fundamental no modelo. Componentes podem rodar só no servidor, não enviam JS pro cliente, podem await direto (assíncronos).
// app/orders/page.tsx (server component por default em Next.js App Router)
async function OrdersPage() {
const orders = await db.query('SELECT ...');
return <OrderList orders={orders} />;
}
Características:
useState, useEffect) nem refs nem eventos.Componentes client (com 'use client') coexistem. RSC pode importar e usar Client Components; o inverso só via children/props.
Modelo de "ilhas": página é majoritariamente HTML estático (RSC), com ilhas de interatividade (Client Components) onde precisa.
Implicação: muito do que se fazia com useEffect pra carregar data agora é await direto no server. getServerSideProps é history.
Estado local: useState/useReducer.
Estado de form: bibliotecas (React Hook Form, formik) ou primitivas + Zod schema.
Estado de servidor (queries, mutations): React Query (TanStack Query) ou SWR. Raramente Redux pra isso.
Estado global (UI): Context simples, ou Zustand, Jotai. Redux Toolkit pra times maiores.
URL state: searchParams. Em Next App Router é nativo.
Rule of thumb moderna: maior parte do "estado global" é estado de servidor. React Query resolve isso com cache, refetch, optimistic updates. Reduz drasticamente código de state global custom.
Ferramentas:
Patterns úteis:
react-virtual, tanstack/react-virtual) pra listas grandes.lazy() + Suspense.startTransition pra inputs que filtram listas pesadas.React.memo seletivo (pós-profiling).useCallback/useMemo).key correta em listas.Anti-patterns:
React.memo por todo lado sem medir.useCallback por todo lado.<Tabs><Tabs.List>...</Tabs.List></Tabs>). Usa Context interno.asChild, similar.Você precisa, sem consultar:
useEffect roda e por que Strict Mode dispara duas vezes.useEffect vs useLayoutEffect com case onde escolher cada.startTransition e quando ele ganha de simplesmente atualizar state.Construir um mini-React funcional o suficiente pra rodar uma TodoApp.
Implementar do zero, em TS:
createElement(type, props, ...children): equivalente a React.createElement. Retorna objeto { type, props, children }.tsconfig jsxFactory: 'createElement' ou pragma similar pra JSX virar suas chamadas.render(element, container), mount inicial.useState: list de hooks por componente, ordem importa.useEffect: roda após commit, com cleanup.useRef.onClick, onInput, etc. Adicionar/remover listeners corretamente em diff.requestIdleCallback + chunked work, à lá Fiber).Object.is é base de comparação shallow.Children tipos, polymorphic components.packages/react-reconciler/src/ReactFiberWorkLoop.js é o coração. Leia.Destrava
02-04 é prereq dos seguintes módulos: