Como fazer em Svelte algo que você já sabe fazer com React
Você provavelmente já se deparou com essa situação:
Está lá, desenvolvendo seu código React feliz, quando o pomodoro toca e você resolve dar uma passeada rápida pelo Twitter.
Lá você vê de novo alguns tweets sobre um tal de "Svelte" que uma galerinha tá falando, mas dá de ombros e segue a vida, afinal ninguém tem tempo para aprender toda hora um novo framework front-end™ né? Ainda mais agora que você acabou de dominar o useMemo
e o useEffect
!
Pois bem, pensando justamente nas pessoas que já conhecem React e não tem tempo (ou paciência, o que é totalmente justificável) para entrar na documentação de um novo framework, mas que ainda possuem aquela curiosidade de brincar com as novidades do nosso ecossistema, eu trago aqui alguns exemplos de...
... como fazer em Svelte algo que você já sabe fazer com React.
Índice
- useState()
- useEffect() e ciclo de vida
- useRef()
- useCallback() e useMemo()
- Variável reativa dependente de outras.
- Binding de campos
- Estilização
- TypeScript
- Condicionais
- Loops
useState()
Vamos começar com o básico.
Uma das features mais legais do React é permitir criar variáveis "reativas", ou seja: caso o valor da variável mude no código, a interface da sua aplicação também muda automaticamente.
No React, fazemos dessa forma:
import { useState } from "react";
const Componente = () => {
const [nome, mudaNome] = useState("pessoa");
const mudaNome = () => {
mudaNome("Maria");
};
return <button onClick={mudaNome}>Olá, {nome}!</button>
};
No Svelte, qualquer variável declarada com let
dentro da tag script
e que for usada no "HTML", se torna reativa automaticamente:
<script>
let nome = "Lex";
const mudaNome = () => {
nome = "Maria";
};
</script>
<button on:click={mudaNome}>Olá, {nome}!</button>
No React, para atualizar a variável nome
, você usa o método mudaNome
por exemplo: mudaNome("Maria")
. No Svelte, você atribui um novo valor direto a variável, por exemplo: nome = "Maria"
.
useEffect()
Com as hooks, as funções componentDidMount
e componentWillUnmount
foram deixadas de lado e a hook useEffect
passou a ser utilizada para lidar com o ciclo de vida de um componente:
import { useEffect } from "react";
const Componente = () => {
useEffect(() => {
// Código roda quando o componente foi criado.
return () => {
// Código roda quando componente vai ser destruído.
};
}, []);
return <></>;
};
No Svelte, temos a função onMount
que faz a mesma coisa:
<script>
import { onMount } from "svelte";
onMount(() => {
// Código roda quando o componente foi criado.
return () => {
// Código roda quando componente vai ser destruído.
};
});
</script>
Alternativamente, você também tem a função onDestroy
que pode ser chamada separadamente, caso você prefira funções de ciclo de vida separadas (o resultado é o mesmo):
<script>
import { onMount, onDestroy } from "svelte";
onMount(() => {
// Código roda quando o componente foi criado.
});
onDestroy(() => {
// Código roda quando componente vai ser destruído.
});
</script>
useRef()
No React, a hook useRef
é usada comumente quando queremos fazer algum tipo de bind em um elemento do DOM, para usá-lo posteriormente.
O motivo de não utilizarmos o próprio useState
para isso, é que cada update em uma variável criada com useState
força o componente inteiro a ser renderizado novamente, e isso além de péssimo para performance, geraria um loop infinito.
Para contornar esses problemas, uma das soluções da equipe do React foi criar uma nova hook (useRef
) que pudesse ser atualizada "indiscriminadamente" sem tanto custo de performance.
No React, um exemplo de uso seria:
import { useRef } from "react";
const Componente = () => {
// elementoCanvas.current -> elemento no DOM
const elementoCanvas = useRef();
return <canvas ref={elementoCanvas}></canvas>;
};
No Svelte, temos a diretriz bind:this
em qualquer elemento HTML, que pode ser utilizada com qualquer variável iniciada com let
:
<script>
// elementoCanvas -> elemento no DOM
let elementoCanvas;
</script>
<canvas bind:this={elementoCanvas}></canvas>
useCallback() e useMemo()
Devido a arquitetura do React de, por padrão, renderizar novamente o componente inteiro sempre que algo é alterado dentro dele (por exemplo, com a hook useState
), são necessárias formas de "preservar" valores e funções entre as renderizações, de forma que esses valores e funções não fiquem sendo executados repetidamente de maneira desnecessária.
É aí que entra o useCallback()
(para preservar funções) e useMemo()
(para preservar valores).
Essas hooks existem pura e simplesmente por uma decisão técnica de arquitetura do React: "Se o componente é re-executado toda vez que algo muda, precisamos de uma forma de otimizar e preservar alguns pedaços desse componente para ele se tornar mais eficiente".
No Svelte, não existe useCallback()
e useMemo()
ou equivalentes porque a arquitetura é diferente. O Svelte não renderiza o componente inteiro a cada atualização dentro dele. Ele atualiza apenas o pedaço do DOM (diretamente, sem Virtual DOM) que sofreu a alteração.
Sendo assim, o Svelte não precisa lidar com os problemas que o useCallback()
e o useMemo()
resolvem no React.
Variável reativa dependente de outras.
As vezes é necessario atualizar uma variável sempre que outras variáveis mudem.
No React, podemos fazer isso com useEffect
:
import { useState, useEffect } from "react";
const Componente = () => {
const [autorizado, mudaAutorizado] = useState(false);
const [idade, mudaIdade] = useState(0);
const [documentosEmDia, mudaDocumentosEmDia] = useState(false);
useEffect(() => {
// Sempre que o state 'idade' ou 'documentosEmDia' mudar,
// essa hook será chamada, atualizando o state 'autorizado'
mudaAutorizado(idade > 25 && documentosEmDia);
}, [idade, documentosEmDia]);
return <></>
};
No Svelte existe a sintaxe $:
que, pasmém, é JavaScript válido (!) e serve para criar variáveis reativas dependentes de outras:
<script>
let idade = 0;
let documentosEmDia = false;
// Sempre que o state 'idade' ou 'documentosEmDia' mudar,
// essa variável será atualizada automaticamente
$: autorizado = idade > 25 && documentosEmDia;
</script>
O compilador do Svelte entende que toda variável que seja iniciada com $:
, deve observar as variáveis depois do =
, e quando uma delas mudar, essa variável é recalculada e atualizada automaticamente.
Binding de campos
Ok, essa provavelmente foi uma das primeiras coisas que você aprendeu em React:
import { useState } from "react";
const Componente = () => {
const [nome, mudaNome] = useState("");
return <input type="text" value={nome} onChange={(e) => mudaNome(e.target.value)} />
};
No Svelte, temos duas opções.
- Da mesma forma que no React:
<script>
let nome = "";
</script>
<input type="text" value={nome} on:input={(e) => nome = e.target.value} />
- A forma "Svelte" (utilizando
bind:value
):
<script>
let nome = "";
</script>
<input type="text" bind:value={nome} />
O bind:value
funciona de forma bilateral. Ou seja, se você mudar o valor direto no campo, ele atualiza a variável. E se você alterar o valor da variável no seu código, o valor do campo é alterado.
O bind:
no Svelte não se restringe apenas ao atributo value
. É possível utilizar binds em diversos atributos e elementos diferentes, inclusive <audio>
, <video>
e <canvas>
.
Estilização
O React deixa a parte de estilização à escolha de quem está desenvolvendo. Você pode usar CSS puro, pré-processadores como SASS ou PostCSS, ou CSS-in-JS.
No Svelte, você pode escrever CSS puro (o framework já "blinda" esse CSS para não vazar para fora do componente, nem para componentes internos):
<p>Olá, mundo!</p>
<style>
/* Esse seletor só aplicará o estilo ao <p> desse componente */
p {
color: red;
}
</style>
Ou, caso prefira, é possível utilizar pré-processadores:
<p>Olá, mundo!</p>
<style lang="scss">
$cor_paragrafo: #f00;
/* Esse seletor só aplicará o estilo ao <p> desse componente */
p {
color: $cor_paragrafo;
}
</style>
Apesar do CSS-in-JS não ser comum no Svelte, ainda assim é possível utilizá-lo sem muita dificuldade.
TypeScript
Tanto React quanto Svelte tem excelente suporte a TypeScript.
No caso do React, após o TypeScript ser instalado e configurado (geralmente pelo próprio CLI com o CRA), basta usar a extensão .tsx
nos seus arquivos para desfrutar das vantagens do TypeScript.
No Svelte, após instalar e configurar (da mesma forma, essa já é uma opção do próprio CLI), basta adicionar lang="ts"
na tag script para habilitar o TypeScript no arquivo.
<script lang="ts">
let name: string;
let cpf: number;
</script>
Condicionais
No React, para fazer um "if" no meio dos elementos, geralmente é utilizada a expressão JavaScript &&
:
import { useState } from "react";
const Componente = () => {
const pseudoAleatorio = +Math.random().toString().substr(2) % 2;
return (
<>
{pseudoAleatorio && <p>É ímpar!</p>}
{!pseudoAleatorio && <p>É par!</p>}
</>
);
};
No Svelte, utilizamos blocos {#if}
:
<script>
const pseudoAleatorio = +Math.random().toString().substr(2) % 2;
</script>
{#if pseudoAleatorio}
<p>É ímpar!</p>
{:else}
<p>É par!</p>
{/if}
Loops
Para fazer loops no React, geralmente usamos a função .map
do JavaScript:
import { useState } from "react";
const Componente = () => {
const pessoas = ["Ana", "Marcos"];
return (
<ul>
{pessoas.map((pessoa) => (
<li>{pessoa}</li>
))}
</ul>
);
};
No Svelte, utilizamos blocos {#each}
:
<script>
const pessoas = ["Ana", "Marcos"];
</script>
<ul>
{#each pessoas as pessoa}
<li>{pessoa}</li>
{/each}
</ul>
Conclusão
Ufa! Depois de muito texto e muito código, deu pra aprender todas as diferenças entre Svelte e React, certo?
Errado!
Tanto React quanto Svelte são mundos inteiros à parte, com diversas funcionalidades, curiosidades e características próprias. Com esse artigo, arranhamos apenas a ponta do iceberg dos truques que o React e o Svelte possuem.
Mas de qualquer forma espero que esse pequeno e humilde comparativo entre essa dupla de biblioteca e framework incríveis tenha aguçado sua curiosidade para continuar explorando as diferenças, semelhanças, convergências e divergências entre ambas!