Olá! Tudo bem? Meu nome é Patrícia Silva, sou instrutora da Escola de Front-end, e quero te dar boas-vindas a mais um curso na Alura.
Audiodescrição: Patrícia se descreve como uma mulher branca de cabelos cacheados pretos e olhos castanho-escuros. Ela usa óculos de armação preta arredondada, veste uma blusa rosa, e está sentada em um ambiente iluminado em azul e lilás, com alguns enfeites ao fundo.
Neste curso, trabalharemos com a aplicação Full Stack CodeConnect, na qual todas as requisições GET já estão implementadas, ou seja, os dados foram integrados ao back-end e são renderizados na tela.
No entanto, ainda precisamos implementar as funcionalidades de Thumbs Up e de adicionar e salvar comentários, persistindo-os tanto na página inicial, que lista todos os posts, quanto na página de detalhes de cada post específico.
A CodeConnect é uma rede social para pessoas desenvolvedoras. Nesse contexto, usaremos as mutations (mutações) do React Query, e o desafio será repleto de emoção, pois vamos trabalhar com técnicas avançadas, como a Optimistic Update, por exemplo.
Com essa técnica, manipulamos o cache para mostrar dados instantaneamente para as pessoas usuárias, melhorando a usabilidade e a experiência na nossa aplicação. Isso também melhora a performance, pois reduz o consumo de processamento do lado do servidor.
Além disso, vamos implementar testes unitários para as mutations, garantindo, assim, a qualidade e o comportamento dessas mutações.
Para acompanhar o conteúdo deste curso, você precisa conhecer React, JavaScript,
async
eawait
, além do básico sobre React Query.
Lembre-se de trazer muita força e energia, pois essa jornada será incrível!
Após seguir os passos da atividade Preparando o ambiente, você terá acesso à página inicial, onde são listados todos os posts da aplicação CodeConnect.
Nosso desafio será incrementar o contador toda vez que a pessoa usuária clicar no botão Thumbs Up (ThumbsUpButton
). Para isso, precisamos utilizar uma mutation (mutação).
ThumbsUpButton
Usamos mutations para criar, atualizar ou deletar dados no servidor, ou se precisarmos realizar um side effect (efeito colateral), ou seja, mudar algo que não é necessariamente no servidor, mas que será uma modificação, a qual podemos exibir em tela.
useMutation
O componente dos posts é o CardPost
. No VS Code, ele está localizado em "src > components > CardPost > index.jsx
".
No topo do arquivo, na linha 1, vamos importar um hook especial chamado useMutation
, que nos ajudará a realizar as mutações.
index.jsx
(src > components > CardPost):
import { useMutation } from "@tanstack/react-query";
// código omitido
O ThumbsUpButton
, para referência, está na linha 31 do mesmo arquivo.
// código omitido
<ThumbsUpButton disable={isFetching} />
// código omitido
Começaremos implementando a mutation antes disso, a partir da linha 13, e depois criaremos um evento no botão.
thumbsMutation
Na linha 13, criaremos uma const
chamada thumbsMutation
. Essa constante irá armazenar tudo que o useMutation()
retornar, então ela será igual a useMutation()
.
// código omitido
export const CardPost = ({ post, highlight, rating, category, isFetching }) => {
const thumbsMutation = useMutation({
});
// código omitido
No escopo de useMutation()
, precisamos passar um objeto de configuração. Esse objeto é muito parecido com o useQuery
: passaremos uma propriedade chamada mutationFn
, que será a função responsável por informar ao useMutation
do React Query onde será performado o efeito colateral, isto é, onde ele deve realizar a criação, a atualização ou a remoção.
Estamos em uma aplicação Full Stack, e temos um endpoint que aceita o método POST para fazer a incrementação e informar qual
ThumbsUpButton
foi incrementado.
Na função mutationFn
, basicamente, vamos receber o argumento, que são os dados do post (postData
). Ela poderia ser declarada em outro arquivo, mas vamos implementar tudo no mesmo local.
// código omitido
const thumbsMutation = useMutation({
mutationFn = (postData) => {
}
});
// código omitido
mutationFn
No escopo da função, na linha 15, adicionaremos um return
seguido de fetch()
. Entre os parênteses, receberemos como argumento o endpoint http://localhost:3000/api/thumbs
.
Após essa string, passaremos um objeto ({}
), que será o objeto normal do fetch()
: o método POST
. Para isso, definimos method
como "POST".
O headers
, que passaremos na linha 17, é um objeto que contém o Content-Type
do tipo application/json
. Em seguida, vamos definir o body
, que será igual a JSON.stringify()
contendo o objeto que vamos passar para essa mutação, ou seja, o postData
da linha 14.
// código omitido
const thumbsMutation = useMutation({
mutationFn = (postData) => {
return fetch("http://localhost:3000/api/thumbs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postData)
});
},
});
// código omitido
onClick
para o botãoNesse momento, temos o thumbsMutation
implementado, fazendo um fetch()
para o endpoint http://localhost:3000/api/thumbs
, e passamos o body
recebendo o objeto postData
.
Agora, o componente ThumbsUpButton
, que irá chamar a mutação, está na linha 41 do arquivo index.jsx
. Na linha 40 logo acima, vamos adicionar um evento onClick
no componente form
.
Nesse evento, passaremos uma função (() =>
) que irá chamar a mutation criada anteriormente. Entre os parênteses da função, vamos colocar um event
, e no escopo, usaremos event.preventDefault()
para evitar que a tela atualize, uma vez que o ThumbsUpButton
está dentro de um formulário.
Para prevenir o comportamento natural, padrão do navegador, usamos a função
preventDefault()
.
Por fim, na linha 43, adicionaremos thumbsMutation.mutate()
. A função mutate()
irá performar a mutação efetivamente, pois o thumbsMutation
retorna várias outras propriedades.
Por exemplo:
isError
,isPending
, entre outras.
No nosso caso, queremos realizar a mutação, então entre os parênteses de mutate()
, passamos o objeto slug
igual a post.slug
, pois o endpoint espera isso para adicionar o Thumbs Up ao post.
// código omitido
<form
onClick={(event) => {
event.preventDefault();
thumbsMutation.mutate({ slug: post.slug });
}}
>
<ThumbsUpButton disable={isFetching} />
// código omitido
Agora vamos acessar o navegador para testar se funcionou. Com a aplicação aberta, vamos teclar "F5" para atualizar e pressionar o botão Thumbs Up do primeiro post, por exemplo. A princípio, nada acontece na página, mas ao atualizar, aparece o número "1" abaixo do ícone.
Vamos o "Inspect" para analisar a aba "Network" e entender o que aconteceu. Quando clicamos no botão, é feita a requisição e a resposta é a seguinte:
{"message":"Thumbs up incremented successfully!"}
Concluímos que a ação foi salva no banco de dados, mas isso não é atualizado automaticamente na UI. Por que isso acontece? Explicaremos no próximo vídeo!
O problema da tela não atualizar a quantidade de Thumbs Up após a mutation ser realizada com sucesso, ocorre porque ainda precisamos performar algumas atualizações.
Sabemos que as queries que buscam dados, ou seja, que realizam o método GET, são armazenadas em cache. Portanto, precisamos informar ao React Query quando ele precisa buscar novos dados. Em outras palavras, devemos dizer:
"Este dado não é mais válido, então, quando precisar dos dados novamente, você precisa acessar a rede para buscar os novos dados no servidor".
No momento, temos duas queries que trazem dados do método POST: uma é posts?page
, ou seja, posts por página (posts?page=1
, posts?page=2
, …); e a outra é post?postId
, post por ID (post?postId=11
, post?postId=12
, …). Precisaremos invalidar essas queries.
useQueryClient
De volta ao VS Code, começaremos importando um hook chamado useQueryClient
. Faremos isso na linha 1 do arquivo index.jsx
da pasta "src > components > CardPost".
index.jsx
(src > components > CardPost):
import { useMutation, useQueryClient } from "@tanstack/react-query";
// código omitido
Feito isso, precisaremos utilizar essa instância, então adicionaremos na linha 12, logo após a declaração de CardPost
, uma const queryClient
igual a useQueryClient()
.
// código omitido
export const CardPost = ({ post, highlight, rating, category, isFetching }) => {
const queryClient = useQueryClient();
// código omitido
onSuccess
Em breve, aplicaremos a constante queryClient
em uma invalidação de queries, mas antes precisamos utilizar alguns callbacks que existem no useMutation()
.
Entre os callbacks do useMutation
, temos, por exemplo, o onSuccess
. Toda vez que a mutation for finalizada com sucesso, esse callback executará a função que vamos adicionar.
O callback
onSuccess
só irá correr quando a mutation for finalizada com sucesso. Afinal, não adianta invalidar dados se houve erro. Porém, existem outros callbacks que iremos abordar no decorrer do curso.
Dito isso, na linha 22, digitaremos onSuccess: () => {}
.
// código omitido
const thumbsMutation = useMutation({
mutationFn: (postData) => {
return fetch("http://localhost:3000/api/thumbs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postData),
});
},
onSuccess: () => {
}
});
// código omitido
No escopo da função, faremos a invalidação das queries que mencionamos anteriormente, para quando a mutation ocorrer com sucesso, invalidarmos os dados. Dessa forma, assim que alguma pessoa usuária acessar essa tela, a query será chamada novamente.
Na linha 23, usaremos o método queryClient.invalidateQueries()
para definir a invalidação da query ["post", post.slug]
.
// código omitido
onSuccess: () => {
queryClient.invalidateQueries(["post", post.slug]);
}
// código omitido
Ainda queremos invalidar uma segunda query: na linha 24, adicionaremos queryClient.invalidateQueries(["posts", currentPage])
.
// código omitido
onSuccess: () => {
queryClient.invalidateQueries(["post", post.slug]);
queryClient.invalidateQueries(["posts", currentPage]);
}
// código omitido
Feito isso, precisamos passar o componente currentPage
para a função de CardPost
, na linha 11.
// código omitido
export const CardPost = ({
post,
highlight,
rating,
category,
isFetching,
currentPage,
}) => {
const queryClient = useQueryClient();
// código omitido
currentPage
Até o momento, ainda não importamos o componente currentPage
. Quem usa o CardPost
é a página inicial, do arquivo "src > app > page.js
".
Na linha 37, temos o currentPage
na função fetchPosts()
, e queremos passá-lo para o CardPost
, a partir da linha 67. Portanto, na linha 73, vamos definir o currentPage
igual a {currentPage}
.
page.js
(src > app):
// código omitido
<CardPost
key={post.id}
post={post}
rating={ratingsAndCartegoriesMap?.[post.id]?.rating}
category={ratingsAndCartegoriesMap?.[post.id]?.category}
isFetching={isFetching}
currentPage={currentPage}
/>
// código omitido
Com o navegador aberto, vamos testar se as queries serão invalidadas e se a UI será atualizada. Após atualizar a página, apertaremos o botão Thumbs Up novamente. Agora ele incrementa perfeitamente.
Perceba que toda vez que clicamos, ele vai até a rede, primeiro para performar a mutation, e depois para invalidar as queries.
Isso não significa que, para invalidar as queries, ele vai para a rede. Após invalidar, como já estamos na página, o dado precisa ser requisitado novamente e ele vai para a rede. Essa é a ordem.
O curso React Query: manipulando interações com mutations possui 138 minutos de vídeos, em um total de 39 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.