Front-end · Pleno

Estado em front-end: props, state e quando subir o estado

Frameworks de componentes (React, Vue, Svelte) giram em torno de uma pergunta: onde mora o dado e quem pode mudá-lo? Acertar isso é a diferença entre uma UI previsível e um emaranhado de bugs onde nada está sincronizado. Estado e props são os dois lados dessa moeda.

Props: a entrada imutável

Props são os dados que um componente recebe de quem o usa. Da perspectiva do componente, são imutáveis: ele lê, mas não altera. Pense numa receita de bolo — os ingredientes vêm de fora, e a receita não pode reescrever a lista de compras; só usar o que chegou.

State: o dado interno mutável

State é o dado que o componente possui e pode mudar ao longo do tempo: o texto de um input, se um modal está aberto, um contador. Quando o state muda, o framework re-renderiza o componente para refletir o novo valor.

function Contador() {
  const [n, setN] = useState(0);
  return (
    <button onClick={() => setN(n + 1)}>
      Cliquei {n}x
    </button>
  );
}
PropsState
OrigemComponente paiO próprio componente
Mutável?Não (só leitura)Sim (via setter)
Quem controlaQuem renderizaO componente

Fluxo de dados unidirecional

Os dados descem de pai para filho via props; os eventos sobem via callbacks. Esse fluxo unidirecional é o que torna a UI previsível: para saber por que algo está na tela, você segue o dado de cima para baixo, numa única direção. É como uma cachoeira — a água só desce.

Lifting state up: subir o estado

E quando dois componentes irmãos precisam do mesmo dado? Você não duplica o state em cada um — isso gera dessincronização. Você sobe o estado (lifting state up) para o ancestral comum mais próximo, que então passa o valor para baixo via props. O dado vive num único lugar: a "fonte da verdade".

function Pai() {
  const [texto, setTexto] = useState('');
  return (
    <>
      <Input valor={texto} aoMudar={setTexto} />
      <Preview valor={texto} />
    </>
  );
}

Estado derivado e inputs controlados

Cuidado com estado derivado: se um valor pode ser calculado a partir de outro state, não o guarde separadamente — calcule na hora. Guardar a lista e também "quantos itens" cria duas fontes da verdade que vão divergir.

Um input controlado é aquele cujo valor vem do state: o que aparece na tela é sempre o que está no state, e digitar dispara um evento que atualiza o state. Isso te dá controle total — validar, formatar, limpar — porque o React é dono do valor, não o DOM.

A regra que evita 90% dos bugs de estado: cada dado tem uma única fonte da verdade. Se o mesmo valor existe em dois lugares, eles vão se desencontrar.

Quando partir para estado global

Subir o estado funciona até você se pegar passando a mesma prop por cinco níveis ("prop drilling") só para chegar lá embaixo. Aí entra estado global (Context, Redux, Zustand, signals): dados que muitas partes distantes da árvore consomem — usuário logado, tema, carrinho. Mas não comece por aí: estado global tem custo de complexidade. Use-o quando o local realmente não escala.

É comum em entrevista pedir para explicar a diferença entre props e state, ou descrever quando você faria lifting state up. No dia a dia, isso aparece toda vez que dois componentes "deveriam mostrar o mesmo número" e não mostram.

Conclusão

Props entram e não mudam; state é interno e muda; dados descem e eventos sobem. Suba o estado quando irmãos compartilham um dado, evite duplicar o que é derivável e só recorra ao global quando o prop drilling doer. Com a UI previsível, o próximo foco é fazê-la renderizar rápido.

Component frameworks (React, Vue, Svelte) revolve around one question: where does the data live and who can change it? Getting this right is the difference between a predictable UI and a tangle of bugs where nothing is in sync. State and props are the two sides of that coin.

Props: the immutable input

Props are the data a component receives from whoever uses it. From the component's perspective, they are immutable: it reads them but doesn't change them. Think of a cake recipe — the ingredients come from outside, and the recipe can't rewrite the shopping list; it can only use what arrived.

State: the mutable internal data

State is the data a component owns and can change over time: an input's text, whether a modal is open, a counter. When state changes, the framework re-renders the component to reflect the new value.

function Contador() {
  const [n, setN] = useState(0);
  return (
    <button onClick={() => setN(n + 1)}>
      Cliquei {n}x
    </button>
  );
}
PropsState
OriginParent componentThe component itself
Mutable?No (read-only)Yes (via setter)
Who controls itWhoever renders itThe component

Unidirectional data flow

Data flows down from parent to child via props; events flow up via callbacks. This unidirectional flow is what makes the UI predictable: to find out why something is on screen, you follow the data from top to bottom, in a single direction. It's like a waterfall — the water only goes down.

Lifting state up

And when two sibling components need the same data? You don't duplicate the state in each — that causes desynchronization. You lift the state up to the nearest common ancestor, which then passes the value down via props. The data lives in a single place: the "source of truth".

function Pai() {
  const [texto, setTexto] = useState('');
  return (
    <>
      <Input valor={texto} aoMudar={setTexto} />
      <Preview valor={texto} />
    </>
  );
}

Derived state and controlled inputs

Beware of derived state: if a value can be computed from another piece of state, don't store it separately — compute it on the fly. Storing the list and also "how many items" creates two sources of truth that will diverge.

A controlled input is one whose value comes from state: what appears on screen is always what's in state, and typing fires an event that updates the state. This gives you full control — validating, formatting, clearing — because React owns the value, not the DOM.

The rule that prevents 90% of state bugs: each piece of data has a single source of truth. If the same value exists in two places, they will drift apart.

When to reach for global state

Lifting state works until you catch yourself passing the same prop through five levels ("prop drilling") just to reach the bottom. That's where global state comes in (Context, Redux, Zustand, signals): data that many distant parts of the tree consume — logged-in user, theme, cart. But don't start there: global state has a complexity cost. Use it when local state truly doesn't scale.

Interviews commonly ask you to explain the difference between props and state, or to describe when you'd lift state up. Day to day, it shows up every time two components "should show the same number" and don't.

Conclusion

Props come in and don't change; state is internal and changes; data flows down and events flow up. Lift state when siblings share data, avoid duplicating what can be derived and only reach for global state when prop drilling hurts. With a predictable UI, the next focus is making it render fast.

Leia também

Voltar à trilha