Componentes e estilos no React Native
Aprenda a criar componentes React Native reutilizáveis, passar props, compor telas e aplicar StyleSheet com um cartão acessível e testado.
Um componente React Native é uma função que recebe informações por props e
devolve uma parte da interface. Nesta lição, você transforma um cartão repetido
em uma peça reutilizável, organiza estilos com StyleSheet e testa a interação
sem precisar tocar manualmente na tela a cada mudança.
Use o projeto da primeira lição ou um projeto Expo SDK 57 equivalente. A meta é sair de uma tela que conhece todos os detalhes e chegar a uma tela que combina peças com responsabilidades claras.
Peças de LEGO com encaixes nomeados
Imagine uma caixa de LEGO. Uma peça pode aparecer em várias construções, mas
cada encaixe aceita uma conexão prevista. No nosso mapa, o componente é a peça,
as props são os encaixes nomeados e a tela é a construção. titulo e
concluido, por exemplo, dizem quais informações o cartão aceita.
O nome técnico é composição de componentes. Em vez de criar uma classe que herda toda a tela, a gente monta componentes menores dentro de outros. O TypeScript descreve o contrato das props e o React entrega os valores de cada instância durante a renderização.
A analogia tem limite: uma peça física não muda depois de encaixada. Um componente pode receber novas props e renderizar outra representação. Voltando ao comportamento técnico, React chama a função novamente quando a entrada relevante muda e reconcilia o novo resultado com a interface existente.
Essa composição faz parte do modelo mostrado no guia de React Native: o componente descreve intenção em JSX, React coordena renderizações e React Native traduz os elementos para cada plataforma. Separar as camadas ajuda a diagnosticar. Uma prop ausente é problema do contrato; uma propriedade visual inexistente pertence à API de estilos; um comportamento diferente no Android pede verificação na plataforma.
Comece pelo contrato que a tela precisa cumprir
Crie src/types/habit.ts:
export type Habit = {
id: string;
title: string;
done: boolean;
};Habit é um alias de tipo. Ele não existe no aplicativo final: serve para o
editor e o compilador conferirem que todo hábito tem id, título e estado de
conclusão. Agora declare apenas o que o cartão precisa receber:
type HabitCardProps = {
habit: Habit;
onToggle: (id: string) => void;
};onToggle é uma callback prop, uma função entregue pelo componente pai. O
cartão avisa que houve intenção de alternar; ele não decide onde os dados são
salvos. Essa fronteira deixa a peça visual simples e testável.
Extraia o cartão sem esconder o que ele faz
Crie src/components/habit-card.tsx:
import { Pressable, StyleSheet, Text, View } from 'react-native';
import type { Habit } from '@/types/habit';
type HabitCardProps = {
habit: Habit;
onToggle: (id: string) => void;
};
export function HabitCard({ habit, onToggle }: HabitCardProps) {
return (
<Pressable
accessibilityLabel={`${habit.title}, ${habit.done ? 'concluído' : 'pendente'}`}
accessibilityRole="checkbox"
accessibilityState={{ checked: habit.done }}
onPress={() => onToggle(habit.id)}
style={({ pressed }) => [styles.card, pressed && styles.pressed]}
>
<View style={[styles.marker, habit.done && styles.markerDone]} />
<Text style={[styles.title, habit.done && styles.titleDone]}>
{habit.title}
</Text>
</Pressable>
);
}Pressable detecta diferentes estados de interação. A prop style recebe uma
função porque o valor pressed muda enquanto o dedo ou mouse pressiona. O array
de estilos é lido da esquerda para a direita: valores posteriores sobrescrevem
propriedades anteriores quando a condição é verdadeira.
Os atributos de acessibilidade não são decoração. accessibilityRole comunica
que a peça se comporta como checkbox, accessibilityState informa se está
marcada, e o label une nome e situação. Um leitor de tela recebe uma descrição
útil, não apenas “botão”.
StyleSheet organiza objetos, não uma folha CSS
Complete o mesmo arquivo:
const styles = StyleSheet.create({
card: {
alignItems: 'center',
backgroundColor: '#ffffff',
borderRadius: 14,
flexDirection: 'row',
gap: 12,
minHeight: 64,
paddingHorizontal: 16,
paddingVertical: 12,
},
pressed: { opacity: 0.72 },
marker: {
borderColor: '#486581',
borderRadius: 10,
borderWidth: 2,
height: 20,
width: 20,
},
markerDone: { backgroundColor: '#2f855a', borderColor: '#2f855a' },
title: { color: '#102a43', flex: 1, fontSize: 17 },
titleDone: { color: '#627d98', textDecorationLine: 'line-through' },
});O nome correto continua sendo estilo, mas o formato não é CSS textual.
StyleSheet.create recebe objetos JavaScript, usa nomes camelCase como
backgroundColor e números sem px para dimensões independentes da densidade.
Nem toda propriedade CSS existe em React Native, e algumas têm comportamento
específico da plataforma.
Pense no StyleSheet como um guarda-roupa catalogado: card é um conjunto
básico, pressed é uma sobreposição temporária e titleDone troca a aparência
quando o dado muda. O limite é que estilos não guardam estado; eles só são
selecionados pela renderização. Quem decide se titleDone entra no array é
habit.done.
Componha duas instâncias na tela
Importe a peça em src/app/index.tsx e passe dados diferentes:
import { StyleSheet, View } from 'react-native';
import { HabitCard } from '@/components/habit-card';
import type { Habit } from '@/types/habit';
const habits: Habit[] = [
{ id: 'agua', title: 'Beber água', done: true },
{ id: 'leitura', title: 'Ler por 20 minutos', done: false },
];
export default function HomeScreen() {
function handleToggle(id: string) {
console.log('alternar', id);
}
return (
<View style={styles.screen}>
{habits.map((habit) => (
<HabitCard habit={habit} key={habit.id} onToggle={handleToggle} />
))}
</View>
);
}
const styles = StyleSheet.create({
screen: { backgroundColor: '#f0f4f8', flex: 1, gap: 12, padding: 24 },
});key ajuda o React a identificar cada item entre renderizações. Ela não chega
como prop ao HabitCard. Para listas maiores, não mantenha map dentro de uma
tela rolável improvisada; a gente usará FlatList na lição de listas.
Entenda a cascata do array de estilos
Você pode provar a ordem com um componente pequeno:
const styles = StyleSheet.create({
base: { color: '#102a43', fontSize: 16 },
warning: { color: '#c53030' },
});
<Text style={[styles.base, true && styles.warning]}>Prazo vencido</Text>;O texto conserva fontSize: 16 de base, mas a cor final é vermelha porque
warning veio depois. Isso é composição de objetos de estilo, não a cascata de
seletores do CSS. Duplicar a propriedade depois é uma decisão explícita e
local.
Evite também usar o StyleSheet como depósito de nomes sem relação com a peça.
card, marker e titleDone dizem qual elemento ou variação recebem. Se dois
componentes compartilham apenas a mesma cor, um token de tema pode ser útil;
compartilhar o objeto inteiro só para remover três linhas cria acoplamento.
Reutilização vale quando preserva uma intenção comum, não apenas quando reduz
caracteres.
Um erro comum é passar texto onde a propriedade espera número:
const styles = StyleSheet.create({
title: { fontSize: '17px' },
});Ao rodar npx tsc --noEmit, a conferência reproduzida foi:
Remova px e mantenha 17. O tipo não garante que a escolha visual ficou boa,
mas impede um formato que a API não aceita.
Teste o comportamento público da peça
Um teste valioso não pergunta qual função interna foi chamada pelo React. Ele interage com o papel acessível que a pessoa encontra. Crie o teste:
import { fireEvent, render, screen } from '@testing-library/react-native';
import { HabitCard } from '@/components/habit-card';
test('envia o id ao pressionar um hábito pendente', () => {
const onToggle = jest.fn();
render(
<HabitCard
habit={{ id: 'agua', title: 'Beber água', done: false }}
onToggle={onToggle}
/>,
);
fireEvent.press(screen.getByRole('checkbox', { name: /beber água, pendente/i }));
expect(onToggle).toHaveBeenCalledWith('agua');
});O comando npm test -- --ci executou essa interação no projeto de prova:
Isso não prova cor, espaçamento ou toque num aparelho real. Prova o contrato observável do componente: ele expõe um checkbox acessível e entrega o id certo quando pressionado.
Missão: crie uma variação sem copiar o componente
Adicione a prop opcional emphasis?: boolean. Quando ela for verdadeira,
aplique uma borda azul; quando for falsa ou ausente, preserve o cartão atual.
Depois renderize apenas o hábito pendente com ênfase.
Pronto quando:
1. HabitCardProps aceita emphasis opcional.
2. O mesmo componente renderiza as duas variações.
3. O teste anterior continua verde.
4. Um novo teste encontra a variação pelo texto e não por detalhes internos.Se você precisou duplicar todo o JSX, volte ao contrato: a mudança provavelmente é uma prop, não um novo componente. A próxima etapa liga essas peças a dados que mudam em estado e formulários no React Native.
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, Expo SDK 57.0.15, React Native 0.86.2, React 19.2.3, TypeScript 6.0.3, Jest 29.7.0 e Testing Library 14.0.1, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- React Native — Core Components and Native Components — reactnative.dev
- React Native — Style — reactnative.dev
- React Native — Pressable — reactnative.dev
- React Native — Accessibility — reactnative.dev


