Atualmente, o React é uma das bibliotecas JavaScript mais populares. Ela pode ser usada para criar aplicativos dinâmicos e responsivos, permite melhor desempenho e pode ser facilmente ampliada. A lógica subjacente é baseada em componentes que podem ser reutilizados em diferentes contextos, reduzindo a necessidade de escrever o mesmo código várias vezes. Em resumo, com o React você pode criar aplicativos eficientes e poderosos.
Portanto, nunca houve um momento melhor para aprender a criar aplicativos React.
No entanto, sem um entendimento sólido de alguns recursos-chave do JavaScript, construir aplicativos React pode ser difícil ou até mesmo impossível.
Por esse motivo, compilamos uma lista de recursos e conceitos do JavaScript que você precisa conhecer antes de começar com o React. Quanto melhor você entender esses conceitos, mais fácil será para você construir aplicativos React profissionais.
JavaScript e ECMAScript
JavaScript é uma linguagem de script popular usada em conjunto com HTML e CSS para criar páginas dinâmicas na web. Enquanto o HTML é usado para criar a estrutura de uma página da web e o CSS para criar o estilo e o layout de seus elementos, o JavaScript é a linguagem usada para adicionar comportamento à página, ou seja, para criar funcionalidade e interatividade.
Desde então, a linguagem foi adotada pelos principais navegadores e um documento foi escrito para descrever a maneira como o JavaScript deveria funcionar: o padrão ECMAScript.
A partir de 2015, uma atualização do padrão ECMAScript é lançada anualmente e, portanto, novos recursos são adicionados ao JavaScript todos os anos.
O ECMAScript 2015 foi a sexta versão do padrão e, portanto, também é conhecido como ES6. As versões seguintes são marcadas em progressão, de modo que nos referimos ao ECMAScript 2016 como ES7, ao ECMAScript 2017 como ES8 e assim por diante.
Para garantir que os recursos mais recentes do JavaScript adicionados ao seu aplicativo JS funcionem como esperado em todos os navegadores da web, você tem três opções:
- Esperar pelo Suporte dos Navegadores: Você pode aguardar até que todos os principais navegadores forneçam suporte para os novos recursos que você está usando. No entanto, isso pode não ser viável se você precisar dos recursos específicos para o seu aplicativo e não puder esperar pela adoção generalizada pelos navegadores.
- Usar um Polyfill: Um polyfill é “um snippet de código (geralmente JavaScript na web) usado para fornecer funcionalidade moderna em navegadores mais antigos que não a suportam nativamente” (consulte também a documentação da MDN (Mozilla Developer Network). Os polyfills preenchem as lacunas de compatibilidade, permitindo que você use os recursos mais recentes do JavaScript, mesmo em navegadores mais antigos.
- Usar um Transpilador JavaScript: Você pode usar um transpilador JavaScript, como o Babel ou o Traceur, que converte o código ECMAScript 2015+ em uma versão JavaScript compatível com todos os navegadores.
Declarações vs expressões
Entender a diferença entre declarações (statements) e expressões (expressions) é essencial ao construir aplicativos React. Vamos voltar aos conceitos básicos de programação por um momento.
Um programa de computador é uma lista de instruções a serem executadas por um computador. Essas instruções são chamadas de declarações.
Ao contrário das declarações, expressões são fragmentos de código que produzem um valor. Em uma declaração, uma expressão é uma parte que retorna um valor e geralmente a vemos no lado direito de um sinal de igual.
Considerando que:
As declarações em JavaScript podem ser blocos ou linhas de código que geralmente terminam com ponto e vírgula ou estão entre chaves.
Aqui está um exemplo simples de uma declaração em JavaScript:
document.getElementById("hello").innerHTML = "Hello World!";
A declaração acima escreve "Hello World!"
em um elemento DOM com id="hello"
.
Como já mencionamos, as expressões produzem um valor ou são elas próprias um valor. Considere o exemplo a seguir:
msg = document.getElementById("hello").value;
document.getElementById("hello").value
é uma expressão, pois retorna um valor.
Um exemplo adicional deve ajudar a esclarecer a diferença entre expressões e declarações:
const msg = "Hello World!";
function sayHello( msg ) {
console.log( msg );
}
No exemplo acima,
- a primeira linha é uma instrução, em que
"Hello World!"
é uma expressão, - a declaração da função é uma instrução, em que o parâmetro
msg
passado para a função é uma expressão, - a linha que imprime a mensagem no console é uma instrução, em que novamente o parâmetro
msg
é uma expressão.
Por que as expressões são importantes no React?
Ao criar um aplicativo React, você pode injetar expressões JavaScript em seu código JSX. Por exemplo, você pode passar uma variável, escrever um manipulador de eventos ou uma condição. Para fazer isso, você precisa incluir seu código JS entre parênteses.
Por exemplo, você pode passar uma variável:
const Message = () => {
const name = "Carlo";
return <p>Welcome {name}!</p>;
}
Em resumo, os parênteses informam ao transpilador que você deve processar o código entre parênteses como código JS. Tudo o que vem antes da tag de abertura <p>
e depois da tag de fechamento </p>
é código JavaScript normal. Tudo o que estiver dentro das tags de abertura <p>
e de fechamento </p>
é processado como código JSX.
Aqui está outro exemplo:
const Message = () => {
const name = "Ann";
const heading = <h3>Welcome {name}</h3>;
return (
<div>
{heading}
<p>This is your dashboard.</p>
</div>
);
}
Você também pode passar um objeto:
render(){
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22',
description: 'Content Writer'
}
return (
<div>
<h2>Welcome {person.name}</h2>
<img
className="card"
src={person.avatar}
alt={person.name}
/>
<p>Description: {person.description}.</p>
</div>
);
}
E abaixo você encontra um exemplo mais abrangente:
render(){
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22?size=original',
description: 'Content Writer',
theme: {
boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', width: '200px'
}
}
return (
<div style={person.theme}>
<img
src={person.avatar}
alt={person.name}
style={ { width: '100%' } }
/>
<div style={ { padding: '2px 16px' } }>
<h3>{person.name}</h3>
<p>{person.description}.</p>
</div>
</div>
);
}
Observe os parênteses duplos nos atributos style
nos elementos img
e div
. Usamos parênteses duplos para passar dois objetos que contêm estilos de cartão e imagem.
Você deve ter notado que, em todos os exemplos acima, incluímos expressões JavaScript em JSX.
Imutabilidade no React
Mutabilidade e imutabilidade são dois conceitos fundamentais na programação funcional e orientada a objetos.
Imutabilidade significa que um valor não pode ser alterado depois de ter sido criado. Mutabilidade significa, é claro, o oposto.
No Javascript, os valores primitivos são imutáveis, o que significa que, uma vez criado, ele não pode ser alterado. Por outro lado, arrays e objetos são mutáveis, pois suas propriedades e elementos podem ser alterados sem atribuir um novo valor.
Há vários motivos para você usar objetos imutáveis no JavaScript:
- Melhor desempenho
- Redução do consumo de memória
- Segurança da thread (thread-safety)
- Facilidade de codificação e depuração
Seguindo o padrão de imutabilidade, uma vez que uma variável ou objeto é atribuído, ele não pode ser reatribuído ou alterado. Quando precisar modificar dados, você deve criar uma cópia deles e modificar seu conteúdo, deixando o conteúdo original inalterado.
A imutabilidade também é um conceito fundamental no React.
A documentação do React afirma que:
O estado de um componente de classe está disponível como
this.state
. O campo state deve ser um objeto. Não altere o estado diretamente. Se você quiser alterar o estado, chame o métodosetState
com o novo estado.
Sempre que o estado de um componente muda, o React calcula se você deve renderizar novamente o componente e atualizar o Virtual DOM. Se o React não tivesse o registro do estado anterior, não poderia determinar se o componente deveria ser renderizado novamente ou não. A documentação do React fornece um excelente exemplo disso.
Quais recursos JavaScript podemos usar para garantir a imutabilidade do objeto de estado no React? Vamos descobrir!
Declarando variáveis
Você tem três maneiras de declarar uma variável em JavaScript: var
, let
, e const
.
A declaraçãovar
existe desde o início do JavaScript. Ela é usada para declarar uma variável com escopo de função ou global, opcionalmente inicializando com um valor.
Ao declarar uma variável usando var
, você pode declarar novamente e atualizar essa variável no escopo global e local. O código a seguir é permitido:
// Declare a variable
var msg = "Hello!";
// Redeclare the same variable
var msg = "Goodbye!"
// Update the variable
msg = "Hello again!"
As declarações var
são processadas antes da execução de qualquer código. Como resultado, declarar uma variável em qualquer lugar do código é equivalente a declará-la no topo. Esse comportamento é chamado de hoisting.
Vale ressaltar que apenas a declaração da variável é “hoisted”, não a inicialização, que ocorre apenas quando o fluxo de controle alcança a instrução de atribuição. Até esse ponto, a variável é undefined
:
console.log(msg); // undefined
var msg = "Hello!";
console.log(msg); // Hello!
O escopo de uma var
declarada com var em uma função JavaScript abrange todo o corpo dessa função.
Isso significa que a variável não é definida no nível do bloco, mas no nível da função inteira. Isso leva a vários problemas que podem tornar seu código JavaScript problemático e de difícil manutenção.
Para corrigir esses problemas, o ES6 introduziu a palavra-chave let
.
A declaração
let
declara uma variável local com escopo de bloco e, opcionalmente, a inicializa com um valor.
Quais são as vantagens de let
em relação ao var
? Aqui estão algumas:
let
declara uma variável com escopo de bloco, enquantovar
declara uma variável globalmente ou localmente para uma função inteira, independentemente do escopo de bloco.- Variáveis globais
let
não são propriedades do objetowindow
. Você não pode acessá-las usandowindow.nomeDaVariavel
. - A variável
let
só pode ser acessada após à sua declaração ser alcançada. A variável não é inicializada até que o fluxo de controle alcance a linha de código onde ela é declarada (as declaraçõeslet
não são “hoisted”). - Tentar redeclarar uma variável com
let
resulta em um erro de sintaxe (SyntaxError
).
Como as variáveis declaradas usando var
não podem ter escopo de bloco, se você definir uma variável usando var
em um loop ou dentro de uma declaração if
, ela poderá ser acessada de fora do bloco, o que pode levar a um código com erros.
O código no primeiro exemplo é executado sem erros. Agora, substitua var
por let
no bloco de código visto acima:
console.log(msg);
let msg = "Hello!";
console.log(msg);
No segundo exemplo, o uso de let
em vez de var
produz um Uncaught ReferenceError
:
O ES6 também introduz uma terceira palavra-chave: const
.
const
é bastante semelhante a let
, mas com uma diferença fundamental:
Considere o exemplo a seguir:
const MAX_VALUE = 1000;
MAX_VALUE = 2000;
O código acima resultaria em um TypeError:
Além disso:
Se você declarar um const
sem dar a ele um valor, ocorrerá o seguinte SyntaxError
(consulte também ES6 In Depth: let e const):
Mas se uma constante for uma array ou um objeto, você poderá editar propriedades ou itens dentro dessa array, ou objeto.
Por exemplo, você pode alterar, adicionar e remover itens da array:
// Declare a constant array
const cities = ["London", "New York", "Sydney"];
// Change an item
cities[0] = "Madrid";
// Add an item
cities.push("Paris");
// Remove an item
cities.pop();
console.log(cities);
// Array(3)
// 0: "Madrid"
// 1: "New York"
// 2: "Sydney"
Mas você não tem permissão para reatribuir a array:
const cities = ["London", "New York", "Sydney"];
cities = ["Athens", "Barcelona", "Naples"];
O código acima resultaria em um TypeError.
Você pode adicionar, reatribuir e remover propriedades e métodos de objetos:
// Declare a constant obj
const post = {
id: 1,
name: 'JavaScript is awesome',
excerpt: 'JavaScript is an awesome scripting language',
content: 'JavaScript is a scripting language that enables you to create dynamically updating content.'
};
// add a new property
post.slug = "javascript-is-awesome";
// Reassign property
post.id = 5;
// Delete a property
delete post.excerpt;
console.log(post);
// {id: 5, name: 'JavaScript is awesome', content: 'JavaScript is a scripting language that enables you to create dynamically updating content.', slug: 'javascript-is-awesome'}
Mas você não tem permissão para reatribuir o próprio objeto. O código a seguir passaria por um Uncaught TypeError
:
// Declare a constant obj
const post = {
id: 1,
name: 'JavaScript is awesome',
excerpt: 'JavaScript is an awesome scripting language'
};
post = {
id: 1,
name: 'React is powerful',
excerpt: 'React lets you build user interfaces'
};
Object.freeze()
Agora concordamos que o uso do const
nem sempre garante uma imutabilidade forte (especialmente quando você trabalha com objetos arrays). Então, como você pode implementar o padrão de imutabilidade em seus aplicativos React?
Primeiro, quando quiser impedir que os elementos de uma array ou as propriedades de um objeto sejam modificados, você pode usar o método estático Object.freeze()
.
Congelar um objeto impede extensões e torna as propriedades existentes não graváveis e não configuráveis. Um objeto congelado não pode mais ser alterado: novas propriedades não podem ser adicionadas, propriedades existentes não podem ser removidas, sua enumerabilidade, configurabilidade, escrevibilidade ou valor não podem ser alterados, e o protótipo do objeto não pode ser reatribuído. O método
freeze()
retorna o mesmo objeto que foi passado como argumento.
Qualquer tentativa de adicionar, alterar ou remover uma propriedade falhará, seja silenciosamente ou lançando um TypeError
, mais comumente no modo estrito.
Você pode usar o Object.freeze()
dessa forma:
'use strict'
// Declare a constant obj
const post = {
id: 1,
name: 'JavaScript is awesome',
excerpt: 'JavaScript is an awesome scripting language'
};
// Freeze the object
Object.freeze(post);
Se agora você tentar adicionar uma propriedade, receberá um Uncaught TypeError
:
// Add a new property
post.slug = "javascript-is-awesome"; // Uncaught TypeError
Quando você tenta reatribuir uma propriedade, obtém outro tipo de TypeError
:
// Reassign property
post.id = 5; // Uncaught TypeError
Você também pode tentar excluir uma propriedade. O resultado será outro TypeError
:
// Delete a property
delete post.excerpt; // Uncaught TypeError
Template Literals
Quando você precisa combinar strings de caracteres com a saída de expressões em JavaScript, geralmente usa o operador de adição +
. No entanto, você também pode usar um recurso do JavaScript que permite incluir expressões em strings de caracteres sem usar o operador de adição: Template Literals.
Template Literals são um tipo especial de strings de caracteres delimitadas por caracteres de backtick (`
).
Em Template Literals, você pode incluir placeholders, que são expressões incorporadas delimitadas por um caractere de dólar e envolvidas por parênteses.
Aqui está um exemplo:
const align = 'left';
console.log(`This string is ${ align }-aligned`);
As strings de caracteres e os placeholders são passados para uma função padrão que executa a interpolação de strings de caracteres para substituir os placeholders e juntar as partes em uma única string de caracteres. Você também pode substituir a função padrão por uma função personalizada.
Você pode usar Template Literals para:
Strings com várias linhas: os caracteres de nova linha fazem parte do template literal.
console.log(`Twinkle, twinkle, little bat!
How I wonder what you’re at!`);
Interpolação de strings: Sem os Template Literals, você só pode usar o operador de adição para combinar a saída de expressões com strings. Veja o exemplo a seguir:
const a = 3;
const b = 7;
console.log("The result of " + a + " + " + b + " is " + (a + b));
É um pouco confuso, não é? Mas você pode escrever esse código de uma forma mais legível e de fácil manutenção usando Template Literals:
const a = 3;
const b = 7;
console.log(`The result of ${ a } + ${ b } is ${ a + b }`);
Mas lembre-se de que há uma diferença entre as duas sintaxes:
Template literals se prestam a vários usos. No exemplo a seguir, usamos um operador ternário para atribuir um valor para um atributo class
.
const page = 'archive';
console.log(`class=${ page === 'archive' ?
'archive' : 'single' }`);
Abaixo, estamos realizando um cálculo simples:
const price = 100;
const VAT = 0.22;
console.log(`Total price: ${ (price * (1 + VAT)).toFixed(2) }`);
Também é possível aninhar Template Literals, incluindo dentro de um espaço reservado ${expression}
(mas use modelos aninhados com cuidado, pois as estruturas complexas de strings podem ser difíceis de ler e manter).
Modelos com tags: Como mencionamos acima, também é possível definir uma função personalizada para realizar a concatenação de strings. Esse tipo de Template Literals é chamado de Tagged Template.
As tags permitem que você analise os Template Literals com uma função. O primeiro argumento de uma função de tag contém uma array de valores de string de caracteres. Os argumentos restantes estão relacionados às expressões.
As tags permitem que você analise Template Literals com uma função personalizada. O primeiro argumento dessa função é uma array de strings de caracteres incluídas no Template Literals, e os outros argumentos são as expressões.
Você pode criar uma função personalizada para executar qualquer tipo de operação nos argumentos do template e retornar a string manipulada. Aqui está um exemplo muito básico de Tagged Template.
const name = "Carlo";
const role = "student";
const organization = "North Pole University";
const age = 25;
function customFunc(strings, ...tags) {
console.log(strings); // ['My name is ', ", I'm ", ', and I am ', ' at ', '', raw: Array(5)]
console.log(tags); // ['Carlo', 25, 'student', 'North Pole University']
let string = '';
for ( let i = 0; i < strings.length - 1; i++ ){
console.log(i + "" + strings[i] + "" + tags[i]);
string += strings[i] + tags[i];
}
return string.toUpperCase();
}
const output = customFunc`My name is ${name}, I'm ${age}, and I am ${role} at ${organization}`;
console.log(output);
O código acima imprime os elementos de arrays de strings
e tags
e, em seguida, coloca os caracteres da string em maiúsculas antes de imprimir a saída no console do navegador.
Arrow Functions
Arrow Functions são uma alternativa às funções anônimas (funções sem nomes) no JavaScript, mas com algumas diferenças e limitações.
As declarações a seguir são exemplos válidos de Arrow Functions:
// Arrow function without parameters
const myFunction = () => expression;
// Arrow function with one parameter
const myFunction = param => expression;
// Arrow function with one parameter
const myFunction = (param) => expression;
// Arrow function with more parameters
const myFunction = (param1, param2) => expression;
// Arrow function without parameters
const myFunction = () => {
statements
}
// Arrow function with one parameter
const myFunction = param => {
statements
}
// Arrow function with more parameters
const myFunction = (param1, param2) => {
statements
}
Você pode omitir os parênteses se passar apenas um parâmetro para a função. Se você passar dois ou mais parâmetros, deverá colocá-los entre parênteses. Aqui está um exemplo disso:
const render = ( id, title, category ) => `${id}: ${title} - ${category}`;
console.log( render ( 5, 'Hello World!', "JavaScript" ) );
A Arrow function de uma linha retornam um valor por padrão. Se você usar a sintaxe de várias linhas, terá de retornar manualmente um valor:
const render = ( id, title, category ) => {
console.log( `Post title: ${ title }` );
return `${ id }: ${ title } - ${ category }`;
}
console.log( `Post details: ${ render ( 5, 'Hello World!', "JavaScript" ) }` );
Uma diferença fundamental entre funções normais e Arrow Functions é que Arrow Functions não possuem seu próprio vínculo com a palavra-chave this
. Se você tentar usar this
em uma Arrow Function, ele será buscado fora do escopo da função.
Para obter uma descrição mais detalhada das Arrow functions e exemplos de uso, leia também a documentação da MDN (Mozilla Developer Network).
Classes
As classes em JavaScript são um tipo especial de função para criar objetos que usam o mecanismo de herança prototípica.
De acordo com a documentação da MDN (Mozilla Developer Network),
Quando se trata de herança, o JavaScript possui apenas um mecanismo: objetos. Cada objeto tem uma propriedade privada que contém um link para outro objeto chamado de seu protótipo. Esse objeto protótipo tem seu próprio protótipo, e assim por diante, até que um objeto com
null
como protótipo seja alcançado.
Assim como acontece com as funções, você tem duas maneiras de definir uma classe:
- Expressão de classe (class expression)
- Declaração de classe (class declaration)
Você pode usar a palavra-chave class
para definir uma classe dentro de uma expressão, conforme mostrado no exemplo a seguir:
const Circle = class {
constructor(radius) {
this.radius = Number(radius);
}
area() {
return Math.PI * Math.pow(this.radius, 2);
}
circumference() {
return Math.PI * this.radius * 2;
}
}
console.log('Circumference: ' + new Circle(10).circumference()); // 62.83185307179586
console.log('Area: ' + new Circle(10).area()); // 314.1592653589793
Uma classe possui um corpo, que é o código incluído entre chaves. É nele que você irá definir o construtor e os métodos, que também são chamados de membros da classe. O corpo da classe é executado no modo estrito (strict mode), mesmo sem usar a diretiva 'strict mode'
.
O método constructor
é usado para criar e inicializar um objeto criado com uma classe sendo executado automaticamente quando a classe é instanciada. Se você não definir um método construtor em sua classe, o JavaScript usará automaticamente um construtor padrão.
Uma classe pode ser estendida usando a palavra-chave extends
.
class Book {
constructor(title, author) {
this.booktitle = title;
this.authorname = author;
}
present() {
return this.booktitle + ' is a great book from ' + this.authorname;
}
}
class BookDetails extends Book {
constructor(title, author, cat) {
super(title, author);
this.category = cat;
}
show() {
return this.present() + ', it is a ' + this.category + ' book';
}
}
const bookInfo = new BookDetails("The Fellowship of the Ring", "J. R. R. Tolkien", "Fantasy");
console.log(bookInfo.show());
Um construtor pode usar a palavra-chave super
para chamar o construtor principal. Se você passar um argumento para o método super()
, esse argumento também estará disponível na classe do construtor principal.
Para que você possa se aprofundar mais nas classes JavaScript e em vários exemplos de uso, consulte também documentação da MDN (Mozilla Developer Network).
As classes são frequentemente usadas para criar componentes React. Normalmente, você não criará suas próprias classes, mas estenderá as classes React incorporadas.
Todas as classes no React têm um método render()
que retorna um elemento React:
class Animal extends React.Component {
render() {
return <h2>Hey, I am a {this.props.name}!</h2>;
}
}
No exemplo acima, Animal
é um componente de classe. Lembre-se de que:
- O nome do componente deve começar com uma letra maiúscula
- O componente deve incluir a expressão
extends React.Component
. Isso dá acesso aos métodos doReact.Component
. - O método
render()
retorna o HTML e é obrigatório.
Após criar seu componente de classe, você pode renderizar o HTML na página:
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Animal name="Rabbit" />;
root.render(element);
A imagem abaixo mostra o resultado na página (você pode vê-lo em ação no CodePen).
Observe, no entanto, que o uso de componentes de classe no React não é recomendado, sendo preferível definir componentes como funções.
A palavra-chave ‘this’
Em JavaScript, a palavra-chave this
é um espaço reservado genérico normalmente usado dentro de objetos, classes e funções, e se refere a diferentes elementos, dependendo do contexto ou do escopo.
A palavra-chave this
pode ser usada no escopo global. Se você digitar this
no console do navegador, obterá:
Window {window: Window, self: Window, document: document, name: '', location: Location, ...}
Você pode acessar qualquer um dos métodos e propriedades do objeto Window
. Portanto, se você executar this.location
no console do navegador, obterá a seguinte saída:
Location {ancestorOrigins: DOMStringList, href: 'https://kinsta.com/', origin: 'https://kinsta.com', protocol: 'https:', host: 'kinsta.com', ...}
Quando você usa this
em um objeto, ele se refere ao próprio objeto. Dessa forma, você pode se referir aos valores de um objeto nos métodos do próprio objeto:
const post = {
id: 5,
getSlug: function(){
return `post-${this.id}`;
},
title: 'Awesome post',
category: 'JavaScript'
};
console.log( post.getSlug );
Agora vamos tentar usar this
em uma função:
const useThis = function () {
return this;
}
console.log( useThis() );
Se você não estiver no modo estrito, obterá:
Window {window: Window, self: Window, document: document, name: '', location: Location, ...}
Mas se você invocar o modo estrito, obterá um resultado diferente:
const doSomething = function () {
'use strict';
return this;
}
console.log( doSomething() );
Nesse caso, a função retorna undefined
. Isso ocorre porque this
em uma função se refere ao seu valor explícito.
Então, como você pode definir this
em uma função?
Primeiro, você pode atribuir manualmente propriedades e métodos à função:
function doSomething( post ) {
this.id = post.id;
this.title = post.title;
console.log( `${this.id} - ${this.title}` );
}
new doSomething( { id: 5, title: 'Awesome post' } );
Mas você também pode usar os métodos call
, apply
e bind
, bem como as arrow functions.
const doSomething = function() {
console.log( `${this.id} - ${this.title}` );
}
doSomething.call( { id: 5, title: 'Awesome post' } );
O método call()
pode ser usado em qualquer função e faz exatamente o que diz: chama a função.
Além disso, call()
aceita qualquer outro parâmetro definido na função:
const doSomething = function( cat ) {
console.log( `${this.id} - ${this.title} - Category: ${cat}` );
}
doSomething.call( { id: 5, title: 'Awesome post' }, 'JavaScript' );
const doSomething = function( cat1, cat2 ) {
console.log( `${this.id} - ${this.title} - Categories: ${cat1}, ${cat2}` );
}
doSomething.apply( { id: 5, title: 'Awesome post' }, ['JavaScript', 'React'] );
const post = { id: 5, title: 'Awesome post', category: 'JavaScript' };
const doSomething = function() {
return `${this.id} - ${this.title} - ${this.category}`;
}
const bindRender = doSomething.bind( post );
console.log( bindRender() );
Uma alternativa às opções discutidas acima é o uso de arrow functions.
As expressões de arrow functions devem ser usadas somente para funções que não sejam de método, pois elas não têm seu próprio
this
.
Isso torna as arrow functions particularmente úteis com manipuladores de eventos.
Isso porque “quando o código é chamado a partir de um atributo de manipulador de eventos em linha, seu this
é definido como o elemento DOM no qual o ouvinte está posicionado” (consulte documentação da MDN (Mozilla Developer Network).
Mas as coisas mudam com as arrow functions porque…
… as arrow functions estabelecem
this
com base no escopo em que a arrow function é definida, e o valorthis
não muda com base em como a função é chamada.
Vinculando “this” aos manipuladores de eventos no React
No contexto do React, você tem algumas maneiras de garantir que o manipulador de eventos não perca seu contexto:
1. Use bind()
dentro do método render:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
showMessage(){
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={ this.showMessage.bind( this ) }>Show message from state!</button> );
}
}
export default MyComponent;
2. Vincule o contexto ao manipulador de eventos no construtor:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
constructor(props) {
super(props);
this.showMessage = this.showMessage.bind( this );
}
showMessage(){
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={ this.showMessage }>Show message from state!</button> );
}
}
export default MyComponent;
3. Defina o manipulador de eventos usando arrow functions:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
showMessage = () => {
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={this.showMessage}>Show message from state!</button> );
}
}
export default MyComponent;
4. Use arrow functions no método de renderização:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
showMessage() {
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={()=>{this.showMessage()}}>Show message from state!</button> );
}
}
export default MyComponent;
Independentemente do método escolhido, quando você clica no botão, o console do navegador mostra a seguinte saída:
This refers to: MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …}
The message is: Hello World!
Operador ternário
O operador condicional (ou operador ternário) permite que você escreva expressões condicionais simples em JavaScript. Ele usa três operandos:
- uma condição seguida de um ponto de interrogação (
?
), - uma expressão a ser executada se a condição for verdadeira, seguida de dois pontos (
:
), - uma segunda expressão a ser executada se a condição for falsa.
const drink = personAge >= 18 ? "Wine" : "Juice";
Também é possível encadear várias expressões:
const drink = personAge >= 18 ? "Wine" : personAge >= 6 ? "Juice" : "Milk";
No entanto, tenha cuidado, pois encadear várias expressões pode levar a um código confuso e difícil de manter.
O operador ternário é particularmente útil no React, especialmente no código JSX, que aceita apenas expressões dentro de chaves.
Por exemplo, você pode usar o operador ternário para definir o valor de um atributo com base em uma condição específica:
render(){
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/...',
description: 'Content Writer',
theme: 'light'
}
return (
<div
className='card'
style={
person.theme === 'dark' ?
{ background: 'black', color: 'white' } :
{ background: 'white', color: 'black'}
}>
<img
src={person.avatar}
alt={person.name}
style={ { width: '100%' } }
/>
<div style={ { padding: '2px 16px' } }>
<h3>{person.name}</h3>
<p>{person.description}.</p>
</div>
</div>
);
}
No código acima, verificamos a condição person.theme === 'dark'
para definir o valor do atributo style
do contêiner div
.
Avaliação de curto-circuito
O operador lógico AND (&&
) avalia os operandos da esquerda para a direita e retorna true
somente se todos os operandos forem true
.
O operador lógico AND é um operador de curto-circuito. Cada operando é convertido em um booleano e, se o resultado da conversão for false
, o operador AND para e retorna o valor original do operando falso. Se todos os valores forem true
, ele retornará o valor original do último operando.
A avaliação de curto-circuito é um recurso JavaScript comumente usado no React, pois permite que você produza blocos de código com base em condições específicas. Aqui está um exemplo:
{
displayExcerpt &&
post.excerpt.rendered && (
<p>
<RawHTML>
{ post.excerpt.rendered }
</RawHTML>
</p>
)
}
No código acima, se displayExcerpt
AND post.excerpt.rendered
for avaliado como true
, o React retornará o bloco final de JSX.
Para recapitular, “se a condição for true
, o elemento logo após &&
aparecerá na saída. Se for false
, o React o ignorará e omitirá”.
Sintaxe de espalhamento (Spread Syntax)
No JavaScript, a sintaxe de espalhamento permite expandir um elemento iterável, como um array ou objeto, em argumentos de função, literais de array ou literais de objeto.
No exemplo a seguir, estamos desempacotando um array em uma chamada de função:
function doSomething( x, y, z ){
return `First: ${x} - Second: ${y} - Third: ${z} - Sum: ${x+y+z}`;
}
const numbers = [3, 4, 7];
console.log( doSomething( ...numbers ) );
Você pode usar a sintaxe de propagação para duplicar uma array (até mesmo arrays multidimensionais) ou para concatenar arrays. Nos exemplos a seguir, concatenamos duas arrays de duas maneiras diferentes:
const firstArray = [1, 2, 3];
const secondArray = [4, 5, 6];
firstArray.push( ...secondArray );
console.log( firstArray );
Alternativamente:
let firstArray = [1, 2, 3];
const secondArray = [4, 5, 6];
firstArray = [ ...firstArray, ...secondArray];
console.log( firstArray );
Você também pode usar a sintaxe de propagação para clonar ou mesclar dois objetos:
const firstObj = { id: '1', title: 'JS is awesome' };
const secondObj = { cat: 'React', description: 'React is easy' };
// clone object
const thirdObj = { ...firstObj };
// merge objects
const fourthObj = { ...firstObj, ...secondObj }
console.log( { ...thirdObj } );
console.log( { ...fourthObj } );
Atribuição de desestruturação
Outra estrutura sintática que você encontrará com frequência usada no React é a sintaxe de atribuição de desestruturação.
No exemplo a seguir, desempacotamos valores de uma array:
const user = ['Carlo', 'Content writer', 'Kinsta'];
const [name, description, company] = user;
console.log( `${name} is ${description} at ${company}` );
E aqui está um exemplo simples de atribuição de desestruturação com um objeto:
const user = {
name: 'Carlo',
description: 'Content writer',
company: 'Kinsta'
}
const { name, description, company } = user;
console.log( `${name} is ${description} at ${company}` );
Mas podemos fazer ainda mais. No exemplo a seguir, desempacotamos algumas propriedades de um objeto e atribuímos as propriedades restantes a outro objeto usando a sintaxe de propagação:
const user = {
name: 'Carlo',
family: 'Daniele',
description: 'Content writer',
company: 'Kinsta',
power: 'swimming'
}
const { name, description, company, ...rest } = user;
console.log( rest ); // {family: 'Daniele', power: 'swimming'}
Você também pode atribuir valores a uma array:
const user = [];
const object = { name: 'Carlo', company: 'Kinsta' };
( { name: user[0], company: user[1] } = object );
console.log( user ); // (2) ['Carlo', 'Kinsta']
Observe que os parênteses ao redor da instrução de atribuição são necessários quando você usa a atribuição de desestruturação literal de objeto sem uma declaração.
Para obter uma análise mais detalhada da atribuição de desestruturação, com vários exemplos de uso, consulte documentação da MDN (Mozilla Developer Network).
filter(), map() e reduce()
O JavaScript fornece vários métodos úteis que você verá serem usados com frequência no React.
filter()
No exemplo a seguir, aplicamos o filtro à array numbers
para obter uma array cujos elementos são números maiores que 5:
const numbers = [2, 6, 8, 2, 5, 9, 23];
const result = numbers.filter( number => number > 5);
console.log(result); // (4) [6, 8, 9, 23]
No exemplo a seguir, obtemos uma array de publicações com a palavra “JavaScript” incluída no título:
const posts = [
{id: 0, title: 'JavaScript is awesome', content: 'your content'},
{id: 1, title: 'WordPress is easy', content: 'your content'},
{id: 2, title: 'React is cool', content: 'your content'},
{id: 3, title: 'With JavaScript to the moon', content: 'your content'},
];
const jsPosts = posts.filter( post => post.title.includes( 'JavaScript' ) );
console.log( jsPosts );
map()
const numbers = [2, 6, 8, 2, 5, 9, 23];
const result = numbers.map( number => number * 5 );
console.log(result); // (7) [10, 30, 40, 10, 25, 45, 115]
Em um componente React, você encontrará com frequência o método map()
usado para criar listas. No exemplo a seguir, estamos mapeando o objeto posts
do WordPress para criar uma lista de artigos:
<ul>
{ posts && posts.map( ( post ) => {
return (
<li key={ post.id }>
<h5>
<a href={ post.link }>
{
post.title.rendered ?
post.title.rendered :
__( 'Default title', 'author-plugin' )
}
</a>
</h5>
</li>
)
})}
</ul>
reduce()
reduce()
aceita dois parâmetros:
- Uma função de callback a ser executada para cada elemento da array. Ela retorna um valor que se torna o valor do parâmetro acumulador na próxima chamada. Na última chamada, a função retorna o valor que será o valor de retorno de
reduce()
. - Um valor inicial que é o primeiro valor do acumulador passado para a função de callback.
A função de callback recebe alguns parâmetros:
- Um acumulador: O valor retornado da chamada anterior para a função de callback. Na primeira chamada, ele é definido como um valor inicial, se especificado. Caso contrário, ele assume o valor do primeiro item da array.
- O valor do elemento atual: O valor é definido como o primeiro elemento da array (
array[0]
) se um valor inicial tiver sido definido; caso contrário, ele assume o valor do segundo elemento (array[1]
). - O índice atual é a posição do índice do elemento atual.
Um exemplo deixará tudo mais claro.
const numbers = [1, 2, 3, 4, 5];
const initialValue = 0;
const sumElements = numbers.reduce(
( accumulator, currentValue ) => accumulator + currentValue,
initialValue
);
console.log( numbers ); // (5) [1, 2, 3, 4, 5]
console.log( sumElements ); // 15
Vamos descobrir em detalhes o que acontece em cada iteração. Volte ao exemplo anterior e altere o endereço initialValue
:
const numbers = [1, 2, 3, 4, 5];
const initialValue = 5;
const sumElements = numbers.reduce(
( accumulator, currentValue, index ) => {
console.log('Accumulator: ' + accumulator + ' - currentValue: ' + currentValue + ' - index: ' + index);
return accumulator + currentValue;
},
initialValue
);
console.log( sumElements );
A imagem a seguir mostra a saída no console do navegador:
Agora vamos ver o que acontece sem o parâmetro initialValue
:
const numbers = [1, 2, 3, 4, 5];
const sumElements = numbers.reduce(
( accumulator, currentValue, index ) => {
console.log( 'Accumulator: ' + accumulator + ' - currentValue: ' + currentValue + ' - index: ' + index );
return accumulator + currentValue;
}
);
console.log( sumElements );
Mais exemplos e casos de uso são discutidos no site de documentação da MDN (Mozilla Developer Network).
Exportações e importações
A partir do ECMAScript 2015 (ES6), é possível exportar valores de um módulo JavaScript e importá-los para outro script. Você usará as importações e exportações extensivamente em seus aplicativos React e, portanto, é importante ter um bom entendimento de como elas funcionam.
O código a seguir cria um componente funcional. A primeira linha importa a biblioteca React:
import React from 'react';
function MyComponent() {
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22?size=original',
description: 'Content Writer',
theme: 'dark'
}
return (
<div
className = 'card'
style = {
person.theme === 'dark' ?
{ background: 'black', color: 'white' } :
{ background: 'white', color: 'black'}
}>
<img
src = { person.avatar }
alt = { person.name }
style = { { width: '100%' } }
/>
<div
style = { { padding: '2px 16px' } }
>
<h3>{ person.name }</h3>
<p>{ person.description }.</p>
</div>
</div>
);
}
export default MyComponent;
Usamos a palavra-chave import
seguida pelo nome que queremos atribuir ao que estamos importando, seguido pelo nome do pacote que queremos instalar, conforme mencionado no arquivo package.json.
Observe que na função MyComponent()
acima, usamos alguns dos recursos JavaScript discutidos nas seções anteriores. Incluímos valores de propriedade entre parênteses e atribuímos o valor da propriedade style
usando a sintaxe do operador condicional.
O script termina com a exportação do nosso componente personalizado.
Agora que você sabe um pouco mais sobre importações e exportações, vamos dar uma olhada mais de perto em como elas funcionam.
Exportação
Cada módulo React pode ter dois tipos diferentes de exportação: exportação nomeada e exportação padrão.
Por exemplo, você pode exportar vários recursos de uma só vez com uma única instrução export
:
export { MyComponent, MyVariable };
Você também pode exportar recursos individuais (function
, class
, const
, let
):
export function MyComponent() { ... };
export let myVariable = x + y;
Mas você só pode ter uma única exportação padrão:
export default MyComponent;
Você também pode usar a exportação padrão para recursos individuais:
export default function() { ... }
export default class { ... }
Importação
Depois que o componente tiver sido exportado, ele poderá ser importado para outro arquivo, por exemplo, um arquivo index.js, com outros módulos:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import MyComponent from './MyComponent';
const root = ReactDOM.createRoot( document.getElementById( 'root' ) );
root.render(
<React.StrictMode>
<MyComponent />
</React.StrictMode>
);
No código acima, usamos a declaração de importação de várias maneiras.
Nas duas primeiras linhas, atribuímos um nome aos recursos importados; na terceira linha, não atribuímos um nome, mas simplesmente importamos o arquivo ./index.css. A última declaração import
importa o arquivo ./MyComponent e atribui um nome.
Vamos descobrir as diferenças entre essas importações.
No total, há quatro tipos de importações:
Importação nomeada
import { MyFunction, MyVariable } from "./my-module";
Importação padrão
import MyComponent from "./MyComponent";
Importação de espaço de nome
import * as name from "my-module";
Importação de efeito colateral
import "module-name";
Após adicionar alguns estilos ao index.css, o cartão deverá ter a aparência da imagem abaixo, na qual você também pode ver o código HTML correspondente:
Observe que as declarações do import
só podem ser usadas em módulos no nível superior (não dentro de funções, classes etc.).
Para obter uma visão geral mais abrangente das declarações import
e export
, você também pode consultar os seguintes recursos:
- export (documentação da MDN (Mozilla Developer Network))
- import (documentação da MDN (Mozilla Developer Network))
- Importação e exportação de componentes (React dev)
- Quando devo usar chaves para importação de ES6? (Stack Overflow)
Resumo
O React é uma das bibliotecas JavaScript mais populares da atualidade e é uma das habilidades mais requisitadas no mundo do desenvolvimento da web.
Com o React, é possível criar aplicativos da web dinâmicos e interfaces avançadas. Criar aplicativos grandes, dinâmicos e interativos pode ser fácil graças a seus componentes reutilizáveis.
Mas o React é uma biblioteca JavaScript, e um bom entendimento dos principais recursos do JavaScript é essencial para começar sua jornada com o React. É por isso que reunimos em um só lugar alguns dos recursos do JavaScript mais comumente usados no React. Dominar esses recursos dará a você uma vantagem em sua jornada de aprendizado do React.
E quando se trata de desenvolvimento web, fazer a transição do JS/React para o WordPress requer muito pouco esforço.
Agora é a sua vez, quais recursos do JavaScript você acha mais úteis no desenvolvimento do React? Deixamos passar alguma coisa importante que você gostaria de ver em nossa lista? Compartilhe suas ideias conosco nos comentários abaixo.
Deixe um comentário