Dia 10 — JSX e o primeiro componente

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

JSX e o primeiro componente

Aula 1

O que e JSX e por que existe

O que é JSX e por que existe

A sintaxe JSX é o que o JavaScript entende e o transformador converte em chamada

de função. Não é HTML, não é XML — apesar do nome, JavaScript XML é o nome do

formato, não uma linguagem. O JSX existe porque descrever tela em árvore de tags

é mais curto que descrever a mesma árvore em chamadas aninhadas.

const { View, Text } = require('react-native');

O que o <Text>ola</Text> realmente é, depois do Babel:

h(Text, null, 'ola');

A tag vira função, o conteúdo entre a tag vira argumento. É por isso que Text

precisa estar importada: View e Text são funções do JavaScript, não tags de um

HTML que não existe aqui.

Componente: a função que devolve JSX

Um componente é uma função que devolve JSX. Não há classe, não há

extends, não há construtor:

function Cartao() {
  return (
    <View>
      <Text>ola</Text>
    </View>
  );
}

Os parênteses não são obrigatórios com um elemento só, mas são com mais de um:

sem eles, o JavaScript leria return seguido de outro comando, e o

SyntaxError sai em Unexpected token.

O componente é chamado pelo runtime, não pelo aluno. Quem chama é o React Native,

quando a tela precisa ser desenhada — e quando o estado muda, ele chama de novo.

O exemplo chama Cartao à mão e lê o que ela devolve, porque não existe

renderizador aqui — mas o objeto devolvido é exatamente o que o React Native

receberia. A primeira linha é nome do componente: Cartao, que vem de

Cartao.name: o nome da função sobrevive no objeto devolvido, e é ele que

aparece quando algo dá erro dentro dela.

A segunda linha, o componente devolveu: View, é a conversão do parágrafo. O

<View> da função virou um objeto cujo .type é a string 'View'. E a terceira

imprime props do View inteiro, com o children sendo outro elemento e não

texto — porque o que foi posto dentro da tag foi <Text>ola</Text>.

Expressão dentro do JSX

Toda expressão em JSX vai entre um par de chaves: {}: as { } marcam expressão de JavaScript, e podem ir em

três lugares:

<Text>{tarefa.texto}</Text>
<Text>{total > 0 ? 'tem' : 'vazio'}</Text>
<Text style={{ fontSize: 18 }}>18</Text>

A primeira linha é o valor da variável, a segunda é uma expressão inteira, e a

terceira é um objeto — que precisa de dois pares de chaves, porque o primeiro par

é a marcação de expressão e o segundo é o objeto.

O exemplo mostra a segunda dessas três, texto do filho: ola. O <Text>ola</Text>

tem o texto escrito direto entre as tags, sem chaves — e mesmo assim o

children dele é 'ola'. É a diferença entre o texto solto e o { }: os dois

viram children, e o solto vira string sem passar por expressão nenhuma.

Quando o conteúdo vem de fora, é o { } que faz o trabalho: filho.props.children

seria o valor da variável, não o nome dela. A regra é que tudo que não for

literal de texto precisa estar entre chaves.

Fragmento: dois elementos sem container

Quando o componente precisa devolver dois elementos irmãos, ele precisa de um

container. O fragmento (<>) é esse container que não gera nada na tela:

function Cabecalho() {
  return (
    <>
      <Text>titulo</Text>
      <Text>subtitulo</Text>
    </>
  );
}

A forma longa é <React.Fragment>. O atalho <> faz o mesmo e é o que se usa

por padrão. Sem o fragmento, a solução seria um View a mais — e ele muda o

desenho, porque View é flex container e empilha os filhos.

children e a chave de lista

O que vai dentro da tag chega na prop children:

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

E quando o elemento está dentro de um map, ele precisa de chave (key).

A chave é o id do item, nunca o índice — o índice anda quando a lista muda de

ordem, e é esse descasamento que faz dois itens trocarem de texto na tela.

O exemplo não usa map — ele monta um elemento só — e é por isso que não tem

key em lugar nenhum. A chave só é exigida quando existem vários irmãos

nascidos da mesma volta do laço. Um elemento único não precisa dela, e

props do View no exemplo sai só com children, sem key.

O uso da chave e do children juntos vem da próxima aula, no componente que

recebe conteúdo por tag e lista os itens por map com key={tarefa.id}.

O que JSX não é

Não é string: '<Text>ola</Text>' é texto de sete caracteres, e não vira

elemento. Não é interpretado em tempo de execução — quem converte é o Metro, no

desenvolvimento, e o Babel no pacote final. Por isso o erro de sintaxe

aparece em tempo de compilação, antes de o app rodar.

E o detalhe de ambiente que este material usa: um arquivo .js com JSX não roda

no Node puro, porque node --check lê <View> como token inesperado. Não é erro

do exemplo — é ausência do transformador. No app quem converte é o Metro.

E é exatamente isso que o exemplo da página mostra: o arquivo tem JSX e roda

mesmo assim, porque existe o passo de transformação antes de o Node ver o

código. Quem rodar o mesmo arquivo pelo caminho comum vai ver

SyntaxError: Unexpected token '<', e o diagnóstico correto é "falta o

Babel", não "falta parêntese".

O props impresso pelo exemplo é a prova do outro lado: ele mostra

'$$typeof': Symbol(react.element) dentro do objeto. Esse símbolo é o que

distingue um elemento de React de um objeto comum, e ele só existe porque o

JSX foi convertido antes.

Exemplo

// Exemplo da aula "O que e JSX e por que existe".
//
// JSX nao e HTML: e uma sintaxe que o JavaScript entende e o
// transformador converte em chamadas de função. Um componente e uma
// função que devolve JSX; o React Native chama essa função e desenha o
// que ela devolveu.
const { View, Text } = require('react-native');

// Componente: uma funcao que devolve JSX. Os parenteses nao sao
// obrigatorios, mas com varios elementos eles sao — sem eles, o
// JavaScript leria `return` seguido de outro comando.
function Cartao() {
  return (
    <View>
      <Text>ola</Text>
    </View>
  );
}

console.log('nome do componente:', Cartao.name);

// O que o componente devolve? Aqui nao tem renderizador, entao o exemplo
// chama a funcao e le o objeto devolvido, que e o mesmo objeto que o
// React Native receberia.
const arvore = Cartao();
console.log('o componente devolveu:', arvore.type);
console.log('props do View:', arvore.props);

// `props.children` e o que foi posto DENTRO do elemento.
const filho = arvore.props.children;
console.log('o View tem um filho:', filho.type);
console.log('texto do filho:', filho.props.children);

Saída real

nome do componente: Cartao
o componente devolveu: View
props do View: {
  children: {
    '$$typeof': Symbol(react.element),
    type: 'Text',
    props: { children: 'ola' },
    children: null
  }
}
o View tem um filho: Text
texto do filho: ola
Aula 2

View, Text e StyleSheet

View, Text e StyleSheet

View é o retângulo que não diz nada. Text é o que escreve texto. Entre os dois

não existe div, p nem span: no React Native, **texto fora de Text não

aparece**, e é o erro de tela em branco que o aluno mais encontra.

const { View, Text } = require('react-native');

A regra que resolve quase tudo: todo texto mora dentro de um Text, e todo

Text mora dentro de um View (ou dentro de outro Text).

StyleSheet: o objeto de estilo

Criar estilo é escrever um objeto JavaScript com propriedades de caixa: flex,

padding, margin, fontSize, color, backgroundColor, borderRadius. Os

nomes são em camelCase, com a mesma semântica de CSS — e o que não muda é o

comportamento padrão, que não é o do navegador: todo View é display: flex

por padrão, e é por isso que filhos empilham em coluna sem nenhuma propriedade.

StyleSheet.create recebe o objeto e devolve o mesmo. A forma recomendada de usar

é uma constante styles fora do componente:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
  },
});

function Tela() {
  return <View style={styles.container}><Text>Ola</Text></View>;
}

style recebe o nome do estilo: style={styles.container}. E a forma

inline — objeto escrito direto no JSX — serve para o valor que muda:

<Text style={{ fontSize: 18, color: '#222' }}>{tarefa.texto}</Text>

O exemplo tem os cinco estilos declarados e o console confirma que existem

todos: `styles disponiveis: [ 'container', 'linha', 'titulo', 'apagado',

'ativo' ]. O Object.keys` é a conferência — e ela mostra que o

StyleSheet.create devolveu um objeto comum, com uma chave por nome declarado.

A forma inline do exemplo é style inline: { flexDirection: 'row', gap: 8 },

com aspas duplas e dois pares de chaves. O valor tem a mesma forma do que está

no StyleSheet — a diferença é onde ele foi escrito: dentro do JSX, na hora da

renderização, e não uma vez para o arquivo inteiro.

Object literal dentro de JSX usa aspas duplas e dois pontos: {{ ... }}. Com

aspas simples e um só par de chaves, a chave vira string e o estilo não é

aplicado — o componente renderiza sem estilo e sem erro nenhum.

As propriedades que resolvem 90% da tela

PropriedadeO que faz
flex: 1ocupa o espaço que sobrou na direção principal
flexDirection'row' na horizontal, 'column' (padrão) na vertical
justifyContentdistribui ao longo do eixo principal
alignItemsalinha no eixo cruzado
padding / marginespaço interno / externo
gapespaço entre filhos, sem margem em cada um
backgroundColorcor de fundo
borderRadiusraio dos cantos

flexDirection: 'row' é o que põe lado a lado — e é a propriedade mais usada em

lista: uma linha com avatar, título e botão.

A árvore do exemplo é o que mostra a tabela em uso. Ela chama o componente

Tela e imprime quatro linhas: tipo da raiz: View,

style da raiz: { flex: 1, padding: 16, backgroundColor: '#fff' },

filho: View | style do filho: { flexDirection: 'row', alignItems: 'center' } e

texto: Estudar.

A última linha é a mais informativa: o caminho até o texto tem três .props

no meio. A raiz é um View; o primeiro .props.children é o View de dentro;

o segundo .props.children é o Text; e o quarto é o texto. São três níveis

de aninhamento para chegar na palavra, e é assim que o JSX vira um objeto

aninhado. Quem debuga tela em branco começa por essa linha: ela mostra o que a

tela montou.

Estilo pode ser array

Quando o estilo vem de uma parte fixa e uma variável, style aceita array,

que o runtime combina na ordem — o último ganha:

<View style={[styles.cartao, ativo && styles.ativo]}>

O && vem porque false não tem estilo: quando a condição é falsa, o item do

array é false e é ignorado. StyleSheet.flatten é o método que transforma esse

array num objeto só, e é ele que devolve { padding: 16 } quando dois estilos

disputam a mesma propriedade.

As quatro linhas do exemplo mostram o array antes e depois do combinado.

style como array: imprime os dois objetos separados por vírgula, com o

container inteiro e o ativo abaixo. `flatten junta num objeto so: { flex: 1,

padding: 16, backgroundColor: '#e8f2ff' }` mostra o resultado: as quatro

propriedades do container e o backgroundColor substituído pelo do ativo.

O && é testado nas duas linhas seguintes, e elas são o par que fixa o

comportamento: com condicional verdadeira: { backgroundColor: '#e8f2ff' } e

com condicional falsa: false. A primeira posição do array recebeu o objeto; a

segunda recebeu false. O runtime ignora o false e combina o resto — é por

isso que false pode ficar no array sem precise de filtro.

O erro de tela em branco

Texto sem Text não renderiza. Estilo com { } só vira string. color sem

contraste dá texto branco em fundo branco — existe, mas some. E

StyleSheet.hairlineWidth não existe: a largura de borda fina é o número 1.

Para descobrir qual dos quatro é, o caminho é o mesmo: colocar console.log

dentro do componente e ler .props do elemento devolvido.

O exemplo fecha com a regra do texto dentro do Text. O componente Cartao

devolve um View com dois Text dentro, e a linha

dois textos: [ 'Treinar', 'hoje as 7' ] mostra os dois textos lidos pelo

map sobre props.children. O children com dois filhos é um array, e é

por isso que o .map funciona — com um filho só, children seria o elemento

único, e não uma lista.

Exemplo

// View, Text e StyleSheet: o componente que monta a tela e o objeto de
// estilo que descreve o desenho.
const { View, Text, StyleSheet } = require('react-native');

/* StyleSheet.create recebe o objeto e devolve o mesmo */
const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
  },
  linha: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  titulo: {
    fontSize: 18,
    color: '#222222',
  },
  apagado: {
    fontSize: 14,
    color: '#888888',
  },
  ativo: {
    backgroundColor: '#e8f2ff',
  },
});

function Tela() {
  return (
    <View style={styles.container}>
      <View style={styles.linha}>
        <Text style={styles.titulo}>Estudar</Text>
      </View>
    </View>
  );
}

const arvore = Tela();
console.log('tipo da raiz:', arvore.type);
console.log('style da raiz:', arvore.props.style);
console.log('filho:', arvore.props.children.type, '| style do filho:', arvore.props.children.props.style);
console.log('texto:', arvore.props.children.props.children.props.children);

/* style inline: objeto com dois pontos e aspas duplas */
const linha = View({ style: { flexDirection: 'row', gap: 8 } });
console.log('style inline:', linha.props.style);

/* estilo pode ser array: o ultimo ganha */
const misto = View({ style: [styles.container, styles.ativo] });
console.log('style como array:', misto.props.style);
console.log('flatten junta num objeto so:', StyleSheet.flatten([styles.container, styles.ativo]));

/* o && entra porque false nao tem estilo: e ignorado no array */
const destaque = true;
const comCondicional = View({ style: [styles.container, destaque && styles.ativo] });
console.log('com condicional verdadeira:', comCondicional.props.style[1]);
const semCondicional = View({ style: [styles.container, false && styles.ativo] });
console.log('com condicional falsa:', semCondicional.props.style[1]);

/* texto sempre dentro de Text */
function Cartao({ titulo, subtitulo }) {
  return (
    <View style={styles.container}>
      <Text style={styles.titulo}>{titulo}</Text>
      <Text style={styles.apagado}>{subtitulo}</Text>
    </View>
  );
}
const cartao = Cartao({ titulo: 'Treinar', subtitulo: 'hoje as 7' });
console.log('dois textos:', cartao.props.children.map((f) => f.props.children));
console.log('styles disponiveis:', Object.keys(styles));

Saída real

tipo da raiz: View
style da raiz: { flex: 1, padding: 16, backgroundColor: '#fff' }
filho: View | style do filho: { flexDirection: 'row', alignItems: 'center' }
texto: Estudar
style inline: { flexDirection: 'row', gap: 8 }
style como array: [
  { flex: 1, padding: 16, backgroundColor: '#fff' },
  { backgroundColor: '#e8f2ff' }
]
flatten junta num objeto so: { flex: 1, padding: 16, backgroundColor: '#e8f2ff' }
com condicional verdadeira: { backgroundColor: '#e8f2ff' }
com condicional falsa: false
dois textos: [ 'Treinar', 'hoje as 7' ]
styles disponiveis: [ 'container', 'linha', 'titulo', 'apagado', 'ativo' ]