Objeto: usar dados de um formulário para fazer uma pesquisa personalizada junto a um sistema externo usando hooks.
Principais termos técnicos abordados: NextJS, SWR, useState(), react hook, form, fetch, async, await, JavaScript, HTML
Requisitos para as instruções funcionarem: ter concluído a Receita5 e estar com o projeto resultante aberto
Requisitos para compreensão das instruções: noções básicas de programação, HTML e NextJS, bem como ter concluído textos anteriores
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
Haverá pouca conversa nessa receita, e mais ação. As receitas anteriores já contiveram bastante texto e são requisito para o acompanhamento desta. A interação com o resultado do que vocês vão fazer será mais ou menos como nesta sequência de imagens:
Abra o projeto resultante da Receita5
No diretório pages, criar o arquivo movies33.js.
Nesse arquivo vamos fazer algo bem parecido com o que fizemos em movies3.js.
Escreva a função TheForm(), como se segue:
export function TheForm(){
return (
<div>
<form>
<label htmlFor="titleSearchString">Filtro de Título</label>
<input id="titleSearchString" name="titleSearchString" type="text" autoComplete="true"/>
</form>
</div>
)
}
Estamos, basicamente, retornando o código HTML correspondente ao formulário que consta nas figuras acima: o rótulo e o campo de entrada (o link, em nosso caso, está fora do formulário).
Acrescente as importações necessárias. São as mesmas da receita anterior.
import useSWR from 'swr'
import {useState} from 'react'
Acrescente as funções TheLink() e TheMovies(), bem parecidas com suas equivalentes na receita anterior.
export function TheMovies({data,show}){
if (!show) return (<div></div>)
if (!data) return (<div></div>)
if (data.error) return (<div>falha na pesquisa</div>)
if (data.Search === '' ) return (<div>carregando...</div>)
return (
<div>
{ data.Search.map( (m) => <div key={m.imdbID}>{m.Title} --- {m.Year}</div> ) }
</div>
)
}
export function TheLink({url, handler}){
return (
<div>
<a href="/movies3.js" onClick={handler}> {url === '' ? 'Mostrar' : 'Ocultar'} </a>
</div>
)
}
Escreva a função Movies33(), exportada por default, trocando o valor ME_SUBSTITUA por sua chave:
export default function Movies33(){
const [state, setState] = useState({url:'', titleSearchString:''})
const {data, error} = useSWR(state.url, async (u) => {
if (!state.url || !state.titleSearchString) return {Search:''}
if (state.url === '' || state.titleSearchString ==='') return {Search:''}
const res = await fetch(`${state.url}/?apiKey=ME_SUBSTITUA&s=${state.titleSearchString}`)
const json = await res.json();
return json;
})
const onClickHandler = e => {
e.preventDefault()
let s = document.getElementById('titleSearchString').value
if (state.url === '') {
setState({url:'http://www.omdbapi.com',titleSearchString:s})
}
else setState({url:'',titleSearchString: state.titleSearchString})
}
return (
<div>
<TheForm/>
<TheLink url={state.url} handler={onClickHandler} />
<TheMovies data={data ? data: {Search:''} } show={state.url !== ''} />
</div>
)
}
Na linha...
const [state, setState] = useState({url:'', titleSearchString:''})
... estamos iniciando o estado da página como um objeto com as propriedades url e titleSearchString. Ambas as propriedades iniciam vazias.
O outro hook é o useSWR()...
const {data, error} = useSWR(state.url, async (u) => {
if (!state.url || !state.titleSearchString) return {Search:''}
if (state.url === '' || state.titleSearchString ==='') return {Search:''}
const res = await fetch(`${state.url}/?apiKey=ME_SUBSTITUA&s=${state.titleSearchString}`)
const json = await res.json();
return json;
})
... e é basicamente a mesma coisa da receita anterior. Com duas diferenças nem tão grandes assim. Uma: estamos definindo a função que vai buscar os dados "na mesma hora" em que chamamos a função useSWR(). Outra: ao chamarmos a função fetch(), estamos calculando uma url de acordo dos os dados do estado que foi obtido pelo useState(). Se, no estado que recuperamos, estiver o atributo url com valor http://www.omdbapi.com e o atributo titleSearchString com o valor redemption, será requisitado o recurso http://www.omdbapi.com/?apikey=ME_SUBSTITUA&s=redemption para o site externo.
- Sim, mas e o formulário? Não fizemos nada com o formulário...
Quando o cara clicar no link, basta modificar o valor do state da página, colocando a url correta e, sobretudo, colocando o valor digitado no campo do formulário como sendo o titleSearchString do state. Como usamos hooks, isso garante que a função que faz uso do hook - a própria Movies33() - será invocada novamente sempre que o state for modificado. Fazemos isso que acabou de ser descrito no seguinte trecho:
const onClickHandler = e => {
e.preventDefault()
let s = document.getElementById('titleSearchString').value
if (state.url === '') {
setState({url:'http://www.omdbapi.com',titleSearchString:s})
}
else setState({url:'',titleSearchString: state.titleSearchString})
}
- E que djabo é esse document na linha document.getElementById('titleSearchString').value ???
O document é um objeto, disponível para qualquer função JavaScript rodando num navegador, um objeto que representa o documento HTML dentro do qual a função é executada. Se você gosta de estrutura de dados, esse elemento é o nodo raiz de uma árvore de objetos que o navegador constrói a partir do processamento do documento HTML que recebeu do servidor web. Cada marca HTML terá um objeto que o represente. Esse document, que é um nodo raiz e que é um objeto como qualquer outro, tem um método de pesquisa getElementById() e a gente passou 'titleSearchString' como parâmetro para esse método. O objeto document, quando esse método é invocado, vai procurar nos nodos abaixo dele algum objeto representando algum elemento HTML com esse identificador recebido como parâmetro. Acontece que lá na função TheForm(), no Passo 3, a gente construiu o campo de texto do formulário justamente com esse identificador. Escrevemos algo como <input id="titleSearchString" ...>
Assim, a chamada document.getElementById('titleSearchString') nos retornará uma representação em objeto JavaScript do campo do formulário. Esse objeto tem um atributo value cujo valor corresponde justamente ao que o usuário escreve no campo.
Assim, apenas empacotamos esse valor num objeto, junto com o valor da url, e chamamos a função que modifica o state. Isso é feito nessa linha:
setState({url:'http://www.omdbapi.com',titleSearchString:s})
Esses objetos, como o document, que estão acessíveis a um JavaScript que roda ao seio de uma página num navegador são o que chamamos objetos DOM - Domain Object Model. Tem um desses objetos para representar cada marca no seu documento, tem objeto até mesmo para representar a janela do navegador e, como JavaScript é ainda um mundo selvagem, tem algumas funções soltas que estão disponíveis, como o setTimeout(), que agenda chamadas futuras, e alert(), que exibe uma mensagem numa caixa de diálogo com um botão OK ao centro. Nada mais anos 90 que o alert(), evite usar esse tipo de código profissionalmente.
E é isso, crianças.
Salvem o arquivo, subam o servidor com o comando npm run dev, acessem o link http://localhost:3000/movies33, testem algumas pesquisas e voltem aqui para os exercícios.
Formulários: https://nextjs.org/blog/forms
Validação de formulários: https://www.youtube.com/watch?v=oD30xo6DGVg
DOM: https://developer.mozilla.org/pt-BR/docs/Web/API/Document_Object_Model
DOM em vídeo: https://www.youtube.com/watch?v=HOv9CqqAZk0
Mais DOM, em vídeo: https://www.youtube.com/watch?v=WWZX8RWLxIk
Se você digitar no formulário o string "dkhjfksd878343" (ou qualquer outro em que a pesquisa por título não retorne nada), você deverá encontrar o seguinte descrito na imagem a seguir. Resolva!
2. Acrescente alguma validação, de forma que o formulário não seja submetido de estiver vazio. Exiba alguma mensagem para o usuário indicando que o campo é obrigatório
3. Faça uma nova página em que, após o usuário teclar enter, a pesquisa é feita sem a necessidade do clique no link.
4. Faça uma nova página, acrescentando mais campos ao formulário, com pesquisas mais específicas.
5. Faça com que a lista de filmes possa ser ordenada por título (a cada clique, a ordenação alterna entre crescente/decrescente).