Objetivo: criar um link que, quando acionado, faz uma requisição personalizada (fixa) a partir do lado cliente ("front-end"), a um sistema externo, atualizando a página quando os resultados chegarem. Usaremos os hooks useState() e useSWR()
Principais termos técnicos abordados: NextJS, SWR, useState(), react hook, fetch, async, await, JavaScript, HTML
Requisitos para as instruções funcionarem: ter concluído a Receita4 e estar com o projeto resultante aberto
Requisitos para compreensão das instruções: noções bem básicas de programação, HTML e NextJS
Como ler essa receita: instruções, dentro dos passos da receita e que requerem ação sua no computador, estarão escritas em cor azul. Comentários sobre os passos estarão em fonte normal, cor preta. Comandos, código-fonte, termos técnicos ou configuração explícita, estarão com fonte diferenciada.
Observação: eventuais instruções de terminal serão comandos Linux. Adapte caso esteja usando outro sistema operacional
O funcionamento da página é simples. Inicialmente, ela exibe apenas um link, Mostrar. Quando esse link é clicado, a página muda o link para Ocultar e mostra, abaixo do link, os resultados de uma consulta, realizada a partir do cliente, a um sistema externo. É uma continuação da Receita4.
Abra o projeto resultante da Receita4
Instalar o pacote react no seu projeto.
No terminal, da raiz do projeto, escreva:
npm install react
Criar um arquivo movies3.js e acrescentar-lhe o seguinte conteúdo:
async function theFetcher(url) {
if (url === null || url === '') return {Search:''}
const res = await fetch(url);
const json = await res.json();
return json;
}
Trata-se de uma função já conhecida na Receita4, não vamos repetir a explicação. Basicamente, interage, nesse caso, a partir do cliente, com um sistema, recebe uma resposta e a retorna como objeto JSON. A estrutura dessa resposta é mais ou menos como o código a seguir:
{
"Search": [
{
"Title": "The Shawshank Redemption",
"Year": "1994",
"imdbID": "tt0111161",
"Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BMDFkYTc0MGEtZmNhMC00ZDIzLWFmNTEtODM1ZmRlYWMwMWFmXkEyXkFqcGdeQXVyMTMxODk2OTU@._V1_SX300.jpg"
},
{
"Title": "The Raid: Redemption",
"Year": "2011",
"imdbID": "tt1899353",
"Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BZGIxODNjM2YtZjA5Mi00MjA5LTk2YjItODE0OWI5NThjNTBmXkEyXkFqcGdeQXVyNzQ1ODk3MTQ@._V1_SX300.jpg"
},
(...)
}
Acrescentar a seguinte função ao movies3.js.
export function TheMovies({data,show}){
if (!show) return (<div></div>)
if (data.error) return (<div>falha na requisição</div>)
if (data.Search === '' ) return (<div>carregando...</div>)
return (
<div>
{ data.Search.map( (m) => <div>{m.Title} --- {m.Year}</div> ) }
</div>
)
}
A função faz algo bem parecido com a função Movies2() da Receita4. A única diferença é que essa função recebe os dados (data, um objeto JSON com os dados de filmes) como parâmetro e Movies2() ia buscar esses dados através de um hook. Por enquanto, não chamamos nenhum hook. Apenas funções "normais".
Acrescentar a seguinte função ao movies3.js:
export function TheLink({url, handler}){
return (
<div>
<a href="/movies3.js" onClick={handler}> {url === '' ? 'Mostrar' : 'Ocultar'} </a>
</div>
)
}
Essa função é um componente (retorna uma marca para ser renderizada) recebe dois parâmetros (rigorosamente, um objeto como com duas propriedades). O primeiro parâmetro é uma URL, o segundo é uma função, handler. Essa função, basicamente, retorna uma marca HTML <a>, que representa um link. Esse link é resultado de processamento sobre os parâmetros. O clique no link chama a função handler, vejam a propriedade onClick da marca.
Já o conteúdo do link, o que o usuário vê e que está dentro da marca <a>, será o texto Mostrar ou Ocultar, a depender do valor do parâmetro url. Se o valor do parâmetro for vazio (''), o conteúdo do link será Mostrar. Caso contrário, o conteúdo será Ocultar. A expressão...
url === '' ? 'Mostrar' : 'Ocultar'
...garante esse comportamento e não deve ser estranha a nenhum estudante de programação com mais de 6 meses de estudos.
Aliás, continuamos sem chamar nenhum hook. Funções recebem parâmetros, processam e retornam algo. Nada mais convencional que isso.
Acrescente no início do arquivo movies3.js o seguinte conteúdo, trocando o termo ME_SUBSTITUA pela sua API key:
import useSWR from 'swr'
import {useState} from 'react'
export default function Movies3(){
const [url, setUrl] = useState('')
const {data, error} = useSWR(url, theFetcher)
const onClickHandler = (e) => {
e.preventDefault()
if (url === '') setUrl('http://www.omdbapi.com/?apikey=ME_SUBSTITUA&s=bagdad')
else setUrl('')
}
return (
<div>
<TheLink url={url} handler={onClickHandler}/>
<TheMovies data={ error?{error:'Erro na pesquisa'}: data ? data: {Search:''} } show={url !== ''}/>
</div>
)
}
- Ah... Agora a coisa ficou mais emocionante!
Então salve o arquivo, suba o servidor com o comando npm run dev, acesse a página http://localhost:3000/movies3, interaja com a página para ver como ela funciona e volte aqui, para a seção de comentários, para entender a função Movies3()
Vamos lá, por partes, comentar a função Movies3().
As linhas...
import useSWR from 'swr'
import {useState} from 'react'
...importam as funções useSWR() e useState(). Isso significa que, em algum momento, devemos usar essas funções em nosso código. Ambas as funções são os chamados hooks, notem que iniciam com o nome use seguido de algum nome iniciado por letra maiúscula. Isso é a especificação para se escrever um hook. Não é a coisa mais linda e formal do mundo, mas é assim, e vocês podem conferir na documentação oficial do react.
- E por que o useState está entre chaves?
Porque não é a função exportada por padrão no pacote.
- Frescura da porra...
Verdade... mas é isso mesmo.
Já as linhas...
const [url, setUrl] = useState('')
const {data, error} = useSWR(url, theFetcher)
...chamam 2 hooks. Um, o useSWR(), a gente já viu como funciona em receita anterior.
O outro é o useState().
- E para que serve o useState() nesse caso específico?
Para que se guarde um valor string (uma url) entre chamadas à função Movies3() (lembrem, não somos nós que chamamos essa função, são componentes NextJS/React) e para que se chame a função Movies3() sempre que esse valor especial seja alterado.
Esse valor especial é chamado de estado, e podemos guardar muitos "valores especiais", chamando a função useState() diversas vezes (o que não fazemos nesse caso).
A função useState() nos retorna duas coisas, num vetor - o valor especial guardado (url), na primeira posição do vetor, e uma função modificadora (setUrl) para que esse valor possa ser alterado. Assim, sempre que chamarmos a função setUrl() passando alguma coisa como parâmetro, essa alguma coisa vai ter seu valor devidamente guardado a as funções de desse valor se utilizam vão ser chamadas novamente, o que vai fazer com que a página seja renderizada novamente e os estados anteriores dos componentes sejam substituídos. Chamadas subsequentes a useState(), claro, retornarão o último valor alterado pela função setUrl().
- e aquele parâmetro '' no const [url, setUrl] = useState('')???
Esse parâmetro é o valor inicial desse valor. É o valor que vai ser retornado, e atribuído a url, na primeira vez que chamarmos o useState(). Esse valor pode ser um número, um string, um objeto... qualquer coisa, basicamente.
Já o trecho a seguir pode até não parecer, mas é uma função:
const onClickHandler = (e) => {
e.preventDefault()
if (url === '') setUrl('http://www.omdbapi.com/?apikey=ME_SUBSTITUA&s=bagdad')
else setUrl('')
}
Uma função para ser chamada quando do clique de um link. Um função de nome onClickHandler, que recebe um evento (e) como parâmetro (o evento é um objeto, tem métodos e atributos).
A primeira coisa que esse objeto faz é chamar o e.preventDefault(). Isso evita que seja dado o comportamento default desse tipo de evento (clique em link), que é o de interagir com o servidor e solicitar uma nova página.
Em vez disso, nós modificamos o nosso estado, através do setUrl(), de acordo com o valor corrente do próprio estado, representado por url.
- E quando o valor 'http://www.omdbapi.com/?apikey=ME_SUBSTITUA&s=bagdad' for passado para o setUrl(), acontece o que?
Acontece que a função Movies3() vai ser chamada novamente e, quando for, o const [url, setUrl] = useState('') vai retornar 'http://www.omdbapi.com/?apikey=ME_SUBSTITUA&s=bagdad' para a constante url.
E, logo em seguida, o outro hook, useSWR(), vai ser chamado e terá o mesmo comportamento que estudamos na Receita4.
Falta apenas montarmos a página com os componentes do link - TheLink - e de exibição de conteúdo - TheMovies. Isso é feito no return:
return (
<div>
<TheLink url={url} handler={onClickHandler}/>
<TheMovies data={ error?{error:'Erro na pesquisa'}: data ? data: {Search:''} } show={url !== ''}/>
</div>
)
Notem que escrevemos a url e o handler como propriedades da marca <TheLink> - e percebam a relação com os parâmetros da função TheLink().
Notem igualmente que escrevemos data (um objeto JSON) e show (um booleano) como propriedades da marca <TheMovies> - e percebam a relação com os parâmetros da função TheMovies()
Percebam, por fim, na execução do projeto, a rapidez com a qual as informações são carregadas a partir do segundo clique em Mostrar. Isso ocorre porque o useSWR() faz cache dos resultados das requisições.
Site oficial: https://pt-br.reactjs.org/docs/hooks-reference.html#usestate
useState(), em vídeo: https://www.youtube.com/watch?v=6WB16wZS61c
Mais useState() em vídeo: https://www.youtube.com/watch?v=kuLF17WL3Qk
Rotas dinâmicas (necessárias nos exercícios): https://nextjs.org/docs/routing/dynamic-routes
Bonitifique sua interface gráfica com bibliotecas de componentes de UI do React. Você pode dar uma olhada aqui: https://ant.design/docs/react/introduce. Há outras opções também: https://pt-br.reactjs.org/community/ui-components.html
Faça com que a listagem dos filmes seja composta por links para uma nova página com a descrição completa do filme. Você deve criar, dentro do diretório pages, um novo diretório de nome onemovie. Dentro de onemovie, você deve criar um arquivo intitulado [id].js (atente para os colchetes!). Esse é o arquivo onde você deve escrever uma página com os dados de um único filme (interface bonitinha, por favor). O fato de você escrever o nome do arquivo entre colchetes o tarna uma rota dinâmica, como um slug dos blogs. Assim, se você acessar o link http:localhost:3000/onemovie/im3556x seus scripts dentro do arquivo [id].js poderão acessar esse valor im3556x que consta na url, fazer uma pesquisa no omdbapi pelo id específico e renderizar as informações do filme. Acesse o link de rotas dinâmicas na seção Para Saber Mais.
Usando o mesmo conhecimento do exercício anterior, crie uma rota dinâmica para pesquisa. Você deve criar um diretório searchmovies dentro do pages e um arquivo [key].js dentro do searchmovies. O conteúdo do arquivo será parecido com a função Movies3(). Sendo que você não precisa exibir o link e deve fazer a pesquisa dos filmes de acordo com o valor de key