af aprenda frontend
módulo 06 componentes

Props: dados de pai para filho.

Passando dados de pai para filho. Props como objeto, desestruturação, valores padrão, children como prop especial.

Props são o mecanismo de comunicação de pai para filho em React. Um componente pai passa dados para um filho como atributos JSX — o filho recebe como objeto no primeiro parâmetro. O fluxo é unidirecional: dados descem, eventos sobem (via callbacks). Entender props é entender como componentes se comunicam.

O que são props

Props são os argumentos de um componente — dados que o pai decide e o filho recebe. Em JSX, props parecem atributos HTML:

tsx
// pai — passa dados como atributos JSX
function App() {
  return (
    <ArticleCard
      titulo="CSS em geral"
      descricao="O que é CSS, anatomia de uma regra e como conectar ao HTML."
      curtidas={42}
      publicado={true}
    />
  );
}

// filho — recebe como objeto no primeiro parâmetro
function ArticleCard(props) {
  return (
    <article>
      <h2>{props.titulo}</h2>
      <p>{props.descricao}</p>
      <span>{props.curtidas} curtidas</span>
    </article>
  );
}
Passando props — pai passa, filho recebe.

A forma mais comum é desestruturar diretamente no parâmetro — deixa explícito quais props o componente usa:

tsx
function ArticleCard({ titulo, descricao, curtidas }: ArticleCardProps) {
  return (
    <article>
      <h2>{titulo}</h2>
      <p>{descricao}</p>
      <span>{curtidas} curtidas</span>
    </article>
  );
}
Desestruturação no parâmetro — idioma padrão.

Props são imutáveis dentro do componente que as recebe. Você não muta props.curtidas diretamente — o pai não veria a mudança e o estado ficaria inconsistente. Quando o filho precisa de estado derivado de uma prop, usa useState (próxima lição).

Props com dados do blog

Para tornar os componentes dinâmicos, passe o objeto Artigo completo como prop:

tsx
import type { Artigo } from "../types";

interface ArticleCardProps {
  artigo: Artigo;
}

export function ArticleCard({ artigo }: ArticleCardProps) {
  const dataFormatada = artigo.publicadoEm
    ? new Intl.DateTimeFormat("pt-BR").format(new Date(artigo.publicadoEm))
    : null;

  return (
    <article className="card-artigo" data-id={artigo.id}>
      <h2 className="card-titulo">{artigo.titulo}</h2>
      <p className="card-descricao">{artigo.descricao}</p>

      <div className="card-meta">
        <span>Por {artigo.autor.nome}</span>
        {dataFormatada && (
          <time dateTime={artigo.publicadoEm ?? ""}>{dataFormatada}</time>
        )}
      </div>

      <div className="card-tags">
        {artigo.tags.map(tag => (
          <span key={tag} className="tag">{tag}</span>
        ))}
      </div>

      <span className="card-curtidas">{artigo.curtidas} curtidas</span>
    </article>
  );
}
ArticleCard.tsx — recebendo o artigo como prop.
tsx
import type { Artigo } from "../types";
import { ArticleCard } from "./ArticleCard";

interface ArticleListProps {
  artigos: Artigo[];
}

export function ArticleList({ artigos }: ArticleListProps) {
  return (
    <section className="lista-artigos">
      {artigos.map(artigo => (
        <ArticleCard key={artigo.id} artigo={artigo} />
      ))}
    </section>
  );
}
ArticleList.tsx — passando artigos como props.

Props opcionais e valores padrão

Props opcionais são declaradas com ? na interface. Valores padrão ficam na desestruturação:

tsx
interface ArticleCardProps {
  artigo: Artigo;
  destaque?: boolean;    // opcional — undefined quando não passado
  compact?: boolean;     // opcional — undefined quando não passado
}

export function ArticleCard({
  artigo,
  destaque = false,   // valor padrão na desestruturação
  compact = false,
}: ArticleCardProps) {
  return (
    <article className={`card-artigo ${destaque ? "card-destaque" : ""} ${compact ? "card-compact" : ""}`}>
      <h2>{artigo.titulo}</h2>
      {!compact && <p>{artigo.descricao}</p>}
    </article>
  );
}

// uso — destaque e compact são opcionais
<ArticleCard artigo={artigo} />                 // destaque=false, compact=false
<ArticleCard artigo={artigo} destaque />        // destaque=true (boolean sem valor)
<ArticleCard artigo={artigo} compact={true} />  // compact=true
Props opcionais e valores padrão.

children — a prop especial

children é a prop que recebe o conteúdo entre as tags de abertura e fechamento do componente. É o que permite criar componentes de layout genéricos:

tsx
import { type ReactNode } from "react";

interface CardProps {
  children: ReactNode;
  className?: string;
}

export function Card({ children, className = "" }: CardProps) {
  return (
    <div className={`card ${className}`}>
      {children}
    </div>
  );
}

// uso — o conteúdo entre as tags vai para children
<Card>
  <h2>Título</h2>
  <p>Descrição do artigo.</p>
</Card>

<Card className="card-destaque">
  <ArticleCard artigo={artigo} />
</Card>
children — conteúdo entre as tags.

ReactNode é o tipo mais amplo para children — aceita JSX, strings, números, null, undefined e arrays deles. Use ReactNode para componentes de layout que precisam aceitar qualquer conteúdo.

Callbacks como props

Para que um filho comunique eventos ao pai, o pai passa uma função como prop:

tsx
interface LikeButtonProps {
  artigoId: number;
  curtido: boolean;
  onCurtir: (id: number) => void;  // callback — pai decide o que fazer
}

export function LikeButton({ artigoId, curtido, onCurtir }: LikeButtonProps) {
  return (
    <button
      className="botao-curtir"
      aria-pressed={curtido}
      onClick={() => onCurtir(artigoId)}  // chama o callback com o id
    >
      {curtido ? "❤️ Curtido" : "🤍 Curtir"}
    </button>
  );
}

// pai — decide o que fazer quando o filho avisa
function ArticleCard({ artigo }: ArticleCardProps) {
  function handleCurtir(id: number) {
    console.log(`Artigo ${id} curtido`);
    // lógica de persistência virá com useState
  }

  return (
    <article>
      <h2>{artigo.titulo}</h2>
      <LikeButton
        artigoId={artigo.id}
        curtido={false}
        onCurtir={handleCurtir}
      />
    </article>
  );
}
Callback como prop — filho avisa o pai de eventos.

O padrão é: estado no pai, evento no filho, callback como ponte. O filho não precisa saber o que acontece depois de chamar onCurtir — ele só avisa. O pai decide.

Resumo

  • Props são os argumentos de um componente — dados passados como atributos JSX pelo pai, recebidos como objeto pelo filho.
  • O fluxo é unidirecional: dados descem (pai → filho via props), eventos sobem (filho → pai via callbacks).
  • Desestruturar no parâmetro é o idioma padrão — deixa explícito o que o componente usa.
  • Props são imutáveis — o filho não muta props. Estado derivado vai para useState.
  • Props opcionais (prop?: Tipo) com valores padrão na desestruturação ({ compact = false }).
  • children: ReactNode — a prop especial para conteúdo entre as tags de abertura e fechamento.
  • Callbacks como props (onCurtir: (id: number) => void) — o filho avisa o pai de eventos via função.
/ checkpoint verifique seu entendimento
questão 1 de 4

Em que direção os dados fluem via props em React?