Hoje, as frameworks reativas, como React e Vue, estão a todo o vapor. A dinâmica na interação, dos dados com as partes comportamentais e visuais, se mostra cada vez mais crucial na criação de uma aplicação, e na usabilidade do usuário.
Neste artigo, vamos desvendar uma maneira bastante promissora, e que te permitirá conhecer melhor como essas funcionalidades reativas podem ser implementadas na sua aplicação em JavaScript. E dar os primeiros passos em direção a recursos que te ajudarão na sua carreira como desenvolvedor.
Digamos que temos o seguinte objeto, e queremos identificar qualquer mudança que ocorra em suas propriedades internas.
const product = {
name: 'Product 1',
price: 9.99
}
Agora, nós desejamos alterar o valor do produto. E para fins práticos, faríamos da seguinte maneira:
product.price = 19.99
Assim que a operação anterior for executada, desejamos que alguma ação seja executada. Por exemplo, alterar o preço do produto disponibilizado no html da página. Para fins didáticos, iremos apenas focar nas funcionalidades do recurso, então mostraremos a mensagem em log.
Utilizando o Proxy do JavaScript para detectar as mudanças
Basicamente, o Proxy é utilizado para definir comportamentos customizados para um objeto. Para instanciar o Proxy, é preciso apenas dois parâmetros, o target ( o objeto a ser observado) e o handler (objeto com as funções que definem o comportamento do proxy).
Por enquanto, focaremos no método set. Ele é executado quando há atribuição de valor em uma propriedade do objeto. O método set disponibiliza três parametros quando chamado:
- target: O objeto que estamos tentando modificar.
- property: A propriedade do objeto que estamos tentado modificar.
- value: O novo valor que estamos inserindo.
Com essas informações, vamos criar o nosso Proxy do objeto product.
const handler = {
set(target, prop, value) {
// Código
},
};
const proxyProduct = new Proxy(product, handler);
Como o intuito é mostrar o log de mudanças realizadas em nosso objeto, iremos complementar o código anterior com um console.log
mostrando a propriedade e os valores alterados. E por fim, atribuiremos o valor ao nosso objeto.
const product = {
name: 'Product 1',
price: 9.99
}
const handler = {
set(target, prop, value) {
console.log(`Mudança realizada na propriedade ${prop} de ${target[prop]} para ${value}.`);
target[prop] = value;
},
};
const proxyProduct = new Proxy(product, handler);
Agora, para testar a funcionalidade, executaremos o seguinte código:
proxyProduct.price = 19.99
// output (log): Mudança realizada na propriedade price de 9.99 para 19.99.
Caso seja realizado a inserção de uma propriedade adicional, que não existe em nosso objeto, o handle também é executado. Exemplo:
proxyProduct.type = 'game'
// output (log): Mudança realizada na propriedade type de undefined para game.
Detectando mudanças em objeto, ou array, interno
A implementação anterior nos permite observar as mudanças realizadas no objeto principal, no primeiro nível do objeto. Porém, normalmente trabalhamos com dados mais complexos, onde a existência de um objeto interno é normal. Da maneira como o código do nosso handler está atualmente, não haverá detectação das mudanças realizadas neste objeto.
Adicionando um novo objeto,
, ao nosso objeto principal: creator
const product = {
name: 'Product 1',
price: 9.99,
creator: {
name: 'Peter Parker',
country: 'France'
}
}
Agora, precisaremos utilizar o método
do handler do Proxy para que possamos identificar a mudança do subnível. Ele é executado toda vez que é realizado um acesso a propriedade do target. get
Exemplo demonstrativo de funcionamento do get para conversão de uma string para maiúscula toda vez que é recuperado o valor da propriedade name do user.
const user = {
name: 'Peter Parker'
}
const handler = {
get (target, prop) {
return target[prop].toLocaleUpperCase()
}
}
const proxyUser = new Proxy(user, handler)
proxyUser.name
// output: PETER PARKER
Agora, voltemos para o nosso projeto principal. Detalharei o código do nosso handler logo a seguir:
const handler = {
get(target, prop) {
if (typeof target[prop] === 'object' && target[prop] !== null) {
return new Proxy(target[prop], handler);
}
return target[prop];
},
set(target, prop, value) {
console.log(`Mudança realizada na propriedade ${prop} de ${target[prop]} para ${value}.`);
target[prop] = value;
},
};
Em resumo, o nosso handler get retorna um novo Proxy para a propriedade que for identificada como um objeto.
Nosso código final
const product = {
name: 'Product 1',
price: 9.99,
creator: {
name: 'Peter Parker',
country: 'France'
}
}
const handler = {
get(target, prop) {
if (typeof target[prop] === 'object' && target[prop] !== null) {
return new Proxy(target[prop], handler);
}
return target[prop];
},
set(target, prop, value) {
console.log(`Mudança realizada na propriedade ${prop} de ${target[prop]} para ${value}.`);
target[prop] = value;
},
};
const proxyProduct = new Proxy(product, handler);
Com modificação anterior em nosso handler, podemos realizar a seguinte alteração no valor do objeto interno.
proxyProduct.creator.country = "Brazil"
// output: Mudança realizada na propriedade country de France para Brazil.
Para finalizar, um exemplo de como atingir uma reatividade simples em seus protótipos. Dependendo da complexidade dos seus dados, você precisará tratar o que será mostrado. Mas a ideia aqui é mostrar uma das muitas abordagens que podem ser implementadas.
set(target, prop, value) {
document.getElementById(prop).textContent = value
target[prop] = value;
},
A proposta do artigo é demonstrar que com a utilização do Proxy do JavaScript, podemos ter acesso a recursos que nos permitem criar aplicações reativas. E, de certa forma, entender também um pouco melhor como atingir a reatividade das frameworks atuais.
Os exemplos demonstrados aqui são para fins didáticos, e precisamos expandir a aplicação desses recursos em funcionalidades mais complexas, como a alteração do HTML quando determinado valor de uma variável muda, alteração de CSS, e daí por diante. Muitas abordagens podem ser implementadas, e tudo dependerá da sua criatividade.