React é uma bibloiteca JavaScript desenvolvida pelo Facebook e Instagram, focada na criação de interfaces gráficas de usuário.
Exemplo Básico: Hello World
Para começar, vamos criar um exemplo simples que exibe "Hello, World!". Este código demonstra a integração básica do React com HTML e o uso do JSX, uma extensão de sintaxe que se parece com HTML.
<html>
<head>
<meta charset="UTF-8">
<title>React Hello World</title>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="appRoot"></div>
<script type="text/babel">
const GreetingElement = <h1>Olá, Mundo!</h1>;
ReactDOM.render(
GreetingElement,
document.getElementById('appRoot')
);
</script>
</body>
</html>
O atributo type="text/babel" no script é crucial para que o navegador interprete o JSX. O JSX permite misturra sintaxe semelhante a HTML com JavaScript.
Iterando com Mapeamento
Podemos usar o método map do JavaScript para renderizar listas de elementos dinamicamente.
const items = ['Maçã', 'Banana', 'Laranja'];
const listElements = items.map((item, index) =>
<li key={index}>{item}</li>
);
ReactDOM.render(
<ul>{listElements}</ul>,
document.getElementById('listContainer')
);
Cada elemento na lista deve ter uma chave única (key), que ajuda o React a identificar quais itens foram alterados, adicionados ou removidos.
Componentização
Componentes são blocos de construção reutilizáveis. Criamos componentes usando React.createClass (ou classes ES6 em abordagens mais modernas). O nome do componente deve começar com uma letra maiúscula.
const CustomButton = React.createClass({
render: function() {
return <button>Botão Personalizado: {this.props.label}</button>;
}
});
ReactDOM.render(
<CustomButton label="Clique Aqui"/>,
document.getElementById('buttonContainer')
);
Props: Propriedades de Componentes
this.props permite que um componente receba dados de seu pai. As propriedades são passadas como atributos no JSX.
const StudentList = React.createClass({
render: function() {
return (
<ol>
{React.Children.map(this.props.children, (child, index) =>
<li key={index}>{child}</li>
)}
</ol>
);
}
});
ReactDOM.render(
<StudentList>
<span>Alice</span>
<span>Bob</span>
</StudentList>,
document.getElementById('studentListContainer')
);
this.props.children refere-se ao conteúdo aninhado dentro de um componente.
PropTypes: Validação de Props
propTypes é usado para definir os tipos esperados das props de um componente e se elas são obrigatórias. Isso ajuda na depuração e na garantia da integridade dos dados.
const UserProfile = React.createClass({
getDefaultProps: function() {
// Define valores padrão para as props
return {
userName: "Usuário Padrão"
};
},
propTypes: {
userName: React.PropTypes.string.isRequired // userName é uma string e é obrigatório
},
render: function() {
return <h1>Perfil de: {this.props.userName}</h1>;
}
});
const userNameValue = "React Developer";
ReactDOM.render(
<UserProfile userName={userNameValue} />,
document.getElementById('profileContainer')
);
DOM Virtual
O DOM Virtual é uma representação em memória do DOM real. O React usa o DOM Virtual para otimizar as atualizações, comparando a versão anterior com a nova e aplciando apenas as mudanças necessárias ao DOM real. Isso resulta em melhor performance.
const InputFocusComponent = React.createClass({
focusInput: function() {
// Acessa o elemento de input diretamente usando 'ref'
const textInput = this.refs.myTextInput;
alert('Valor atual: ' + textInput.value);
textInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<button onClick={this.focusInput}>Focar no Campo</button>
</div>
);
}
});
ReactDOM.render(
<InputFocusComponent />,
document.getElementById('inputFocusContainer')
);
refs é um mecanismo para acessar nós do DOM ou instâncias de componentes.
Estado do Componente (State)
this.state armazena dados que podem mudar ao longo do tempo e afetam a renderização do componente. Mudanças no estado disparam uma nova renderização.
const ToggleButton = React.createClass({
getInitialState: function() {
return { isActive: false };
},
handleToggle: function() {
// Use this.setState para atualizar o estado
this.setState({ isActive: !this.state.isActive });
},
render: function() {
const statusText = this.state.isActive ? 'Ativo' : 'Inativo';
return (
<p onClick={this.handleToggle}>
Status: {statusText}. Clique para alternar.
</p>
);
}
});
ReactDOM.render(
<ToggleButton />,
document.getElementById('toggleButtonContainer')
);
Formulários Controlados
Em React, formulários geralmente são "controlados", o que significa que o estado do componente React gerencia o valor do input.
const SimpleForm = React.createClass({
getInitialState: function() {
return { inputValue: 'Valor inicial' };
},
handleInputChange: function(event) {
this.setState({ inputValue: event.target.value });
},
render: function() {
return (
<div>
<input
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
<p>Valor digitado: {this.state.inputValue}</p>
</div>
);
}
});
ReactDOM.render(
<SimpleForm />,
document.getElementById('formContainer')
);
Ciclo de Vida do Componente
Componentes React passam por diferentes fases: montagem (inserção no DOM), atualização (re-renderização) e desmontagem (remoção do DOM). Cada fase tem métodos que podem ser "ganchos" (hooks) para executar código em momentos específicos.
- Montagem:
componentWillMount,componentDidMount - Atualização:
componentWillUpdate,componentDidUpdate - Desmontagem:
componentWillUnmount
const LifecycleLogger = React.createClass({
componentWillMount: function() { console.log('O componente está prestes a ser montado.'); },
componentDidMount: function() { console.log('O componente foi montado no DOM.'); },
componentDidUpdate: function() { console.log('O componente foi atualizado.'); },
componentWillUnmount: function() { console.log('O componente está prestes a ser desmontado.'); },
render: function() { return <div>Componente com Logs do Ciclo de Vida</div>; }
});
const ControlLifecycle = React.createClass({
mountComponent: function() {
ReactDOM.render(<LifecycleLogger />, document.getElementById('lifecycleTarget'));
},
unmountComponent: function() {
ReactDOM.unmountComponentAtNode(document.getElementById('lifecycleTarget'));
},
render: function() {
return (
<div>
<button onClick={this.mountComponent}>Montar Componente</button>
<button onClick={this.unmountComponent}>Desmontar Componente</button>
<div id="lifecycleTarget"></div>
</div>
);
}
});
ReactDOM.render(<ControlLifecycle />, document.getElementById('lifecycleControlContainer'));
Requisições AJAX
É comum buscar dados de APIs externas. O método componentDidMount é um local ideal para iniciar essas requisições. Após receber os dados, usamos this.setState para atualizar a interface.
// Supondo que jQuery esteja disponível para $.get
const GitHubGists = React.createClass({
getInitialState: function() {
return { username: '', lastGistUrl: '' };
},
componentDidMount: function() {
const apiUrl = this.props.source;
$.get(apiUrl, (result) => {
if (this.isMounted()) { // Verifica se o componente ainda está montado
const firstGist = result[0];
this.setState({
username: firstGist.owner.login,
lastGistUrl: firstGist.html_url
});
}
}).fail((jqXHR, textStatus, errorThrown) => {
console.error("Erro ao buscar dados:", textStatus, errorThrown);
if (this.isMounted()) {
this.setState({ username: 'Erro', lastGistUrl: '#' });
}
});
},
render: function() {
return (
<div>
Último gist de {this.state.username}:
<a href={this.state.lastGistUrl} target="_blank">Link</a>
</div>
);
}
});
ReactDOM.render(
<GitHubGists source="https://api.github.com/users/octocat/gists" />,
document.getElementById('gistContainer')
);
Trabalhando com Promises
Promises são uma forma moderna de lidar com operações assíncronas. Podemos passá-las como props para componentes que gerenciam seus estados (loading, success, error).
const GithubRepoList = React.createClass({
getInitialState: function() {
return { loading: true, error: null, repos: [] };
},
componentDidMount() {
this.props.repoPromise
.then(data => {
this.setState({ loading: false, repos: data.items });
})
.catch(error => {
this.setState({ loading: false, error: error.message });
});
},
render: function() {
if (this.state.loading) {
return <span>Carregando repositórios...</span>;
}
if (this.state.error) {
return <span>Erro: {this.state.error}</span>;
}
const repoItems = this.state.repos.map((repo, key) => (
<li key={key}>
<a href={repo.html_url}>{repo.name}</a> - {repo.description} ({repo.stargazers_count} estrelas)
</li>
));
return (
<div>
<h2>Repositórios Populares de JavaScript no GitHub</h2>
<ul>{repoItems}</ul>
</div>
);
}
});
// Exemplo de como chamar:
const searchUrl = 'https://api.github.com/search/repositories?q=javascript&sort=stars';
ReactDOM.render(
<GithubRepoList repoPromise={$.getJSON(searchUrl)} />,
document.getElementById('repoListContainer')
);