Dia 11 — Props e composição de componentes

Informatica · Conteudo · publicado em 05/10/2026
Dia 11 de 13

Props e composição de componentes

Aula 1

Props: passar dado para o componente

Props: passar dado para o componente

A propriedade do componente é a prop. Props são os dados que um componente

recebe de quem o usa. É o caminho único

de informação de um nível da árvore para o outro: o pai monta o componente com

propriedades, o filho lê.

<Rotulo texto="Estudar" />

Passar props é escrever o nome entre a tag e o valor entre aspas ou entre

chaves. O componente recebe as props como um único objeto, no primeiro

parâmetro:

function Rotulo({ texto, cor }) {
  return <Text style={{ color: cor }}>{texto}</Text>;
}

Os { } ali dentro do parâmetro são o destructuring de props: a

desestruturação das props, em que o nome de

cada variável é o nome da prop. É a forma quase obrigatória em React Native,

porque componente costuma receber cinco ou seis props e ler props.texto

inteiro o tempo todo cansa.

O exemplo mostra essa leitura em uma linha: props nomeadas: Estudar. O

componente devolveu um Text, e o .props.children dele é 'Estudar' — o

valor da prop texto, e não o nome dela. A linha chama o componente passando um

objeto comum, o que o runtime faz quando um pai escreve `<Rotulo texto="Estudar"

/>`.

O detalhe é que a função não tem nome de parâmetro: o primeiro parâmetro é o

objeto das props, e o { texto, cor } é a desestruturação dele. Chamar

Rotulo({ texto: 'Estudar' }) e Rotulo({ texto: 'Estudar', cor: '#222' })

funciona igual — o que não for passado simplesmente não existe no objeto.

children: a prop que vem do conteúdo

O que é posto dentro da tag chega na prop children, sem precisar nomear:

<Rotulo>Estudar</Rotulo>          // children = 'Estudar'

Equivale a passar children à mão. As duas formas funcionam, e a diferença é

quando o conteúdo é longo: com children, o texto fica visível dentro da tag.

Como children é uma prop como outra qualquer, dá para dar valor padrão:

function Cartao({ titulo, children = 'sem conteúdo' }) {
  return <View><Text>{titulo}</Text>{children}</View>;
}

E dá para tratar os dois casos: quando o pai não manda nada, o componente

mostra o conteúdo padrão.

O exemplo escreve o par nas duas linhas: children por tag: 2 dias e

children padrao: sem conteudo. A primeira é o Cartao recebendo a prop

children com o texto '2 dias'; a segunda é o mesmo componente sem a prop, e o

padrão entrou no lugar. A leitura é props.children[1] — o índice 1 porque o

View tem dois filhos: o Text do título e o conteúdo.

O índice 1 é o detalhe que costuma travar: o children de um elemento com

vários filhos é um array, e é por isso que ele se lê com colchete. Com um

filho só, children seria o elemento, e children[1] seria undefined.

Valor padrão e nome opcional

Prop que nem sempre vem precisa de valor padrão, senão o que falta é undefined:

function Botao({ texto, tipo = 'primario', aoClicar }) {
  return <Pressable onPress={aoClicar}><Text>{texto}</Text></Pressable>;
}

O padrão só entra se a prop não vier. Se vier undefined explicitamente, o

padrão entra também; se vier null, não — o null é um valor real.

Prop cujo nome muda de nome entre o pai e o filho é só a renomeação da

desestruturação: { texto: titulo }.

As duas linhas do exemplo tratam dos dois casos do parágrafo e da renomeação.

padrao: primario | quando a prop vem: secundario mostra o padrão entrando e

sendo substituído. null e um valor real, nao entra o padrao: null é o null

que o parágrafo avisa: ele passou direto, e o 'primario' não entrou.

E renomeado: Treinar | sem titulo mostra o nome trocado com o padrão junto: o

pai mandou texto, o filho leu titulo, e sem prop nenhuma o padrão

'sem titulo' apareceu. O : e o = convivem na mesma chave, que é a mesma

combinação da aula de desestruturação.

Espalhar as props

O spread de props passa tudo o que chegou, sem escrever cada nome:

<View {...props} />

É o que permite um componente que serve de caixa transparente: ele recebe o que

o pai mandou e repassa inteiro, acrescentando o que é dele:

function Linha({ children, ...resto }) {
  return <View style={styles.linha} {...resto}>{children}</View>;
}

O ...resto é o rest da aula de desestruturação: tudo que não foi pego pelo

nome vai para resto. O resto tem que vir depois das props nomeadas, senão

sobrescreve o que o componente definiu.

A linha do exemplo é props repassadas: [ 'onPress', 'testID', 'children' ]. O

Caixa pegou children pelo nome e o resto foi repassado com ...resto; o

Object.keys do props devolvido mostra as três chaves que chegaram ao

View de dentro. O children aparece na lista porque ele também estava no

objeto de entrada.

Repare no que o resto não traz: o style que o Caixa definiu no View

não aparece nessa lista, porque o Object.keys foi chamado no elemento **de

fora**, que só recebeu o repassado. É a linha do parágrafo sobre a ordem: se o

{...resto} viesse antes do style, o resto escreveria por cima.

Props são só de leitura

Prop é entrada do componente. O filho lê, mas quem muda é o pai — quem avisa é o

componente filho, chamando a função que recebeu por prop:

<Item tarefa={tarefa} aoConcluir={() => marcarFeita(tarefa.id)} />

É a regra que faz o estado funcionar: o Item não guarda se a tarefa está feita.

Ele recebe tarefa.feita, desenha conforme o valor, e avisa a intenção pelo

aoConcluir. Quem guarda o dado novo é o componente que tem o useState.

A consequência prática é que uma prop nunca deve ser reescrita: `props.texto =

'x'` não muda nada na tela, porque o valor vem do pai e o React Native redesenha

quando o pai passa prop nova.

O exemplo fecha os três pontos da regra. `item montado: [ 'estudar',

'pendente' ] mostra o Item` desenhando exatamente o que veio na prop: o texto

da tarefa e o status derivado de tarefa.feita. avisos recebidos pelo pai: [ 1 ]

mostra o caminho do outro lado — o pai entregou a função aoConcluir, o filho a

chamou, e o 1 que apareceu é o id que estava dentro dela. E

o filho so desenha o que veio de prop: [ 'estudar', 'pendente' ] repete a

primeira linha depois do aviso, para mostrar que o filho não guardou nada.

A última linha é a de leitura: `prop antes: original | o que o filho viu:

original. O pai tinha um objeto com texto: 'original'`, o filho recebeu o

mesmo valor, e nenhuma escrita aconteceu no meio. Para mudar o texto, quem

escreve é o pai — passando prop nova, que é o que dispara o redesenho.

Exemplo

// Props: como o pai passa dado para o filho, e como o filho le.
const { View, Text, StyleSheet } = require('react-native');

const styles = StyleSheet.create({
  linha: { flexDirection: 'row', alignItems: 'center', padding: 12 },
  titulo: { fontSize: 16, color: '#222222' },
});

/* props nomeadas: o primeiro parametro e o objeto das props */
function Rotulo({ texto, cor }) {
  return <Text style={{ color: cor }}>{texto}</Text>;
}
console.log('props nomeadas:', Rotulo({ texto: 'Estudar', cor: '#222' }).props.children);

/* valor padrao do parametro */
function tipoDoBotao({ tipo = 'primario' }) {
  return tipo;
}
console.log('padrao:', tipoDoBotao({}), '| quando a prop vem:', tipoDoBotao({ tipo: 'secundario' }));
console.log('null e um valor real, nao entra o padrao:', tipoDoBotao({ tipo: null }));

/* children: o que vai dentro da tag */
function Cartao({ titulo, children = 'sem conteudo' }) {
  return (
    <View style={styles.linha}>
      <Text style={styles.titulo}>{titulo}</Text>
      {children}
    </View>
  );
}
console.log('children por tag:', Cartao({ titulo: 'Estudar', children: '2 dias' }).props.children[1]);
console.log('children padrao:', Cartao({ titulo: 'Estudar' }).props.children[1]);

/* renomear a prop na desestruturacao */
function Card({ texto: titulo = 'sem titulo' }) {
  return <Text>{titulo}</Text>;
}
console.log('renomeado:', Card({ texto: 'Treinar' }).props.children, '|', Card({}).props.children);

/* spread de props e rest: repassar tudo sem escrever cada nome */
function Caixa({ children, ...resto }) {
  return <View {...resto}>{children}</View>;
}
const repassado = Caixa({ children: 'conteudo', onPress: () => {}, testID: 'caixa' });
console.log('props repassadas:', Object.keys(repassado.props));

/* o pai monta o filho com os dados */
const tarefa = { id: 1, texto: 'estudar', feita: false };
function Item({ tarefa, aoConcluir }) {
  return (
    <View style={styles.linha}>
      <Text style={styles.titulo}>{tarefa.texto}</Text>
      <Text>{tarefa.feita ? 'feita' : 'pendente'}</Text>
    </View>
  );
}
const item = Item({ tarefa, aoConcluir: () => {} });
console.log('item montado:', item.props.children.map((f) => f.props.children));

/* a funcao de aviso: o pai e quem muda o dado, o filho so avisa */
const avisos = [];
const marcarFeita = () => avisos.push(tarefa.id);
const comAviso = Item({ tarefa, aoConcluir: marcarFeita });

// o filho recebe a funcao como prop e a chama: e isso que avisa o pai
marcarFeita();
console.log('avisos recebidos pelo pai:', avisos);
console.log('o filho so desenha o que veio de prop:',
  comAviso.props.children.map((f) => f.props.children));

/* prop reescrita por dentro nao muda nada: quem manda e o pai */
function Recurso({ texto }) {
  return <Text>{texto}</Text>;
}
const props = { texto: 'original' };
console.log('prop antes:', props.texto, '| o que o filho viu:', Recurso(props).props.children);

Saída real

props nomeadas: Estudar
padrao: primario | quando a prop vem: secundario
null e um valor real, nao entra o padrao: null
children por tag: 2 dias
children padrao: sem conteudo
renomeado: Treinar | sem titulo
props repassadas: [ 'onPress', 'testID', 'children' ]
item montado: [ 'estudar', 'pendente' ]
avisos recebidos pelo pai: [ 1 ]
o filho so desenha o que veio de prop: [ 'estudar', 'pendente' ]
prop antes: original | o que o filho viu: original
Aula 2

Composição e componente reutilizável

Composição e componente reutilizável

Composição é montar tela juntando componente pequeno, em vez de escrever um

componente grande. A árvore do app é uma árvore de peças: o componente da tela usa

um componente de linha, que usa um componente de botão.

Reutilizar é o objetivo: componente reutilizável é o que serve em mais de um lugar. A pergunta que

decide se vale extrair é "isto depende do nome da tela?" — se não depende, é o

componente que se repete.

function Botao({ texto, aoClicar, tipo = 'primario' }) { }

O mesmo componente entra em duas telas sem ser copiado. A lista de props do componente é curta e previsível; o que não entra é dado

de uma tela: Botao com tarefa.id dentro é componente com nome próprio, não

reutilizável.

Lista de componente: map com key

Para renderizar lista, o map de componente é o caminho: quando o componente

reutilizável é um item, ele recebe um array e desenha um por elemento:

function Lista({ tarefas }) {
  return (
    <View>
      {tarefas.map((tarefa) => (
        <Item key={tarefa.id} tarefa={tarefa} />
      ))}
    </View>
  );
}

A chave do item (key) é obrigatória nesse map. Ela diz ao React Native

qual elemento da tela corresponde a qual item do array quando a lista muda de

ordem, de tamanho ou de conteúdo. Sem a chave, dois itens trocam de lugar e a tela

mostra o texto trocado.

A chave é o id, nunca o índice. O índice anda quando a lista anda, e a

key={indice} dá exatamente o bug que a chave existe para evitar.

O exemplo monta a lista de verdade e confirma as duas coisas na mesma sequência.

quantos filhos: 3 é a lista de três tarefas produzindo três elementos, e

cada um ja e o Item desenhado: View, View, View mostra que os três são

View — porque o Item já foi chamado e devolveu o seu View. A lista não

guarda referência ao Item; guarda o que ele devolveu.

Duas linhas leem os dados de cada item:

o texto que cada Item recebeu: [ 'estudar', 'treinar', 'descansar' ] e

o status desenhado: [ 'pendente', 'feita', 'pendente' ]. A segunda é a que

mostra o componente decide desenho a partir da prop: o item 2 é o único com

feita: true, e é o único que desenhou 'feita'.

A linha props de um item: [ 'style', 'children' ] responde a uma pergunta que

o parágrafo deixa de fora. A key não aparece ali: o runtime consome a

chave ao montar o elemento, e ela não vira prop. Por isso a key se escreve no

JSX e não se lê depois.

E o par do fim do exemplo é o id contra o índice:

key por indice: 0, 1, 2 e key por id: 1, 2, 3. As duas listas têm os mesmos

três itens, e as chaves são as mesmas posições — a diferença é o que acontece

quando a lista muda de ordem: com o índice, o item que estava em 2 vira

chave 1 e o runtime procura o elemento errado.

Componente pai e componente filho

O pai é quem tem o estado e monta os filhos; o filho é quem só desenha e

avisa. A regra que organiza o código inteiro do curso:

  • quem tem o dado novo é quem tem o useState;
  • quem manda o dado para baixo é o pai, por prop;
  • quem avisa que algo aconteceu é o filho, chamando a função que recebeu por prop.
// pai: guarda e atualiza
<Lista tarefas={tarefas} aoAlternar={() => alternar()} />

// filho: desenha e avisa
<Item tarefa={tarefa} aoConcluir={aoAlternar} />

O filho não sabe onde o dado mora e não precisa saber. É por isso que o mesmo

Item serve dentro de Lista, de Detalhe ou de uma tela de busca.

O exemplo demonstra a regra inteira numa linha só:

id avisado: [ 1 ] | antes: false | depois: true. O filho chamou a função que

recebeu por prop; a função do pai empurrou o 1 no array de avisos e devolveu

uma lista nova com o item trocado. O antes: false e o depois: true são o

mesmo item lido em dois momentos.

A linha seguinte é a garantia do padrão:

o array original nao foi mexido: false | o novo e outro objeto: true. O

tarefas original ainda tem a primeira tarefa como feita: false, e o item na

lista nova é outro objeto. O pai não reescreveu nada — ele produziu uma lista

nova, e é essa troca que o React Native entende como mudança.

Esse { ...t, feita: !t.feita } dentro do map é o spread da aula de

desestruturação: o objeto novo nasce com todas as propriedades do antigo e a

feita trocada. Sem o spread, o item novo teria só o campo feita.

Componente com children: o outro lado da composição

Um componente com children não sabe o que vai dentro dele, e é isso que o torna

reutilizável:

<Cartao titulo="Estatísticas">
  <Text>12 tarefas</Text>
  <Text>3 concluídas</Text>
</Cartao>

Cartao cuida da borda, do raio e do espaçamento; quem passa o conteúdo decide o

conteúdo. Esse par — componente com estrutura fixa e children para o conteúdo —

é o padrão de composição em tela.

O exemplo sai nas três linhas: titulo do cartao: Estatisticas, `conteudo

vindo de children: 12 tarefas e cartao sem conteudo: vazio`. A segunda é o

conteúdo que veio pela prop, lido em children[1]; a terceira é o padrão do

parágrafo aparecendo: sem prop children, o Cartao devolveu 'vazio'.

O Cartao do exemplo não sabe o que está dentro — recebe o texto '12 tarefas'

como children e o coloca no lugar. Trocar por dois Text não mudaria nada no

componente. É essa ignorância sobre o conteúdo que o torna reutilizável.

Quando não extrair

Três sinais de que não vale a pena extrair:

  1. Usado uma vez só e sem previsão de reuso — a indireção custa mais que a

duplicação.

  1. Nome genérico sem comportamento — Caixa que só embrulha coisa é o

<View> com nome novo.

  1. Leva e traz o mesmo dado — o pai lê o filho pela prop que ele acabou de

passar, e aí o componente virou um useState mal colocado.

O sinal de que está na hora de extrair é o oposto: a mesma linha de JSX se

repete com diferença de um nome, ou o StyleSheet tem duas entradas com o mesmo

valor de fontSize. Aí a duplicação já custa.

O parágrafo fala de Caixa que só embrulha coisa, e o exemplo da aula anterior

tem exatamente esse componente — Caixa que recebe {...resto} e repassa. Ele

existe ali para mostrar o rest, e o console mostra que ele produz

[ 'onPress', 'testID', 'children' ]: três nomes repassados sem o componente

saber o que são. Isso é reuso de verdade — o componente não conhece nenhuma das

props que repassa.

O sinal que decide é o da lista de props: se a lista do componente tem nome de

tela dentro — tarefa, usuario, pedido — ele já não é reutilizável, e o nome

diz por quê. Item com tarefa prop é componente de linha de tarefa, não

componente de linha.

Props que mudam com o item

O componente de linha costuma receber o item inteiro (tarefa={tarefa}) em vez

de dez props soltas. É mais fácil de estender: quando a tarefa ganhar um campo

novo, o filho já recebe. E o objeto que o filho nunca deve guardar é o que o pai

passa — o filho desenha, o pai decide.

O exemplo passa o objeto inteiro, e a lista de props do View devolvido

confirma que só duas coisas sobreviveram ao desenho: style e children. As

props do Item — tarefa e aoConcluir — não estão ali: o componente leu o

objeto e não o guardou.

O lado do "campo novo" é o alternar do exemplo: o item novo nasce pelo spread

do item antigo, e o t tinha id e texto. Se a tarefa ganhasse um campo

prioridade, ele estaria no item novo sem nenhuma alteração no componente.

Exemplo

// Composicao: o componente reutilizavel, a lista com map e key, e a
// regra de quem guarda o dado e quem so desenha.
const { View, Text, StyleSheet } = require('react-native');

const styles = StyleSheet.create({
  cartao: { padding: 12, backgroundColor: '#ffffff', borderRadius: 8 },
  linha: { flexDirection: 'row', alignItems: 'center', padding: 12 },
  titulo: { fontSize: 16, color: '#222222' },
});

const tarefas = [
  { id: 1, texto: 'estudar', feita: false },
  { id: 2, texto: 'treinar', feita: true },
  { id: 3, texto: 'descansar', feita: false },
];

/* o filho: so desenha o que veio por prop */
function Item({ tarefa, aoConcluir }) {
  return (
    <View style={styles.linha}>
      <Text style={styles.titulo}>{tarefa.texto}</Text>
      <Text>{tarefa.feita ? 'feita' : 'pendente'}</Text>
    </View>
  );
}

/* o pai: recebe a lista e monta um Item por elemento */
function Lista({ tarefas, aoAlternar }) {
  return (
    <View>
      {tarefas.map((tarefa) => (
        <Item key={tarefa.id} tarefa={tarefa} aoConcluir={aoAlternar} />
      ))}
    </View>
  );
}

const lista = Lista({ tarefas, aoAlternar: () => {} });
const filhos = lista.props.children;
console.log('quantos filhos:', filhos.length);
console.log('cada um ja e o Item desenhado:', filhos.map((f) => f.type).join(', '));
console.log('o texto que cada Item recebeu:', filhos.map((f) => f.props.children[0].props.children));
console.log('o status desenhado:', filhos.map((f) => f.props.children[1].props.children));

/* a key nao vira prop: o runtime consome a chave do elemento ao montar.
   Por isso ela se escreve no JSX, e nao da para ler depois. */
console.log('props de um item:', Object.keys(filhos[0].props));

/* a key e o id, nunca o indice: o indice anda quando a lista anda */
const porIndice = tarefas.map((tarefa, indice) => ({ id: indice, texto: tarefa.texto }));
console.log('key por indice:', porIndice.map((t) => t.id).join(', '));
console.log('key por id:', tarefas.map((t) => t.id).join(', '));

/* o componente com children: estrutura fixa, conteudo livre */
function Cartao({ titulo, children = 'vazio' }) {
  return (
    <View style={styles.cartao}>
      <Text style={styles.titulo}>{titulo}</Text>
      {children}
    </View>
  );
}
const cartao = Cartao({ titulo: 'Estatisticas', children: '12 tarefas' });
console.log('titulo do cartao:', cartao.props.children[0].props.children);
console.log('conteudo vindo de children:', cartao.props.children[1]);
console.log('cartao sem conteudo:', Cartao({ titulo: 'Vazio' }).props.children[1]);

/* o pai guarda o dado novo e o filho so avisa */
let listaAtual = tarefas;
const avisos = [];
function alternar(id) {
  avisos.push(id);
  listaAtual = listaAtual.map((t) => (t.id === id ? { ...t, feita: !t.feita } : t));
  return listaAtual;
}
function ItemAvisa({ tarefa, aoConcluir }) {
  return <View><Text>{tarefa.texto}</Text></View>;
}
const marcado = ItemAvisa({ tarefa: listaAtual[0], aoConcluir: () => alternar(listaAtual[0].id) });
marcado;
alternar(1);
console.log('id avisado:', avisos, '| antes:', tarefas[0].feita, '| depois:', listaAtual[0].feita);
console.log('o array original nao foi mexido:', tarefas[0].feita, '| o novo e outro objeto:', listaAtual[0] !== tarefas[0]);

/* o mesmo componente em duas telas: e a reutilizacao */
const tela1 = Lista({ tarefas: listaAtual });
const tela2 = Lista({ tarefas: tarefas.slice(0, 1) });
console.log('mesmo componente, duas telas:', tela1.props.children.length, 'e', tela2.props.children.length, 'itens');

Saída real

quantos filhos: 3
cada um ja e o Item desenhado: View, View, View
o texto que cada Item recebeu: [ 'estudar', 'treinar', 'descansar' ]
o status desenhado: [ 'pendente', 'feita', 'pendente' ]
props de um item: [ 'style', 'children' ]
key por indice: 0, 1, 2
key por id: 1, 2, 3
titulo do cartao: Estatisticas
conteudo vindo de children: 12 tarefas
cartao sem conteudo: vazio
id avisado: [ 1 ] | antes: false | depois: true
o array original nao foi mexido: false | o novo e outro objeto: true
mesmo componente, duas telas: 3 e 1 itens