roger-melo-treinamentos / curso-de-js-roger-melo

Repositório de informações do CJRM
491 stars 170 forks source link

Aplicação: [Quiz interativo] #6743

Closed DevNeves closed 1 year ago

DevNeves commented 1 year ago

As cores da sua versão da aplicação são diferentes das cores da aplicação mostrada na aula?

Sim

Sua aplicação contém funcionalidades que não foram mostradas nas aulas da aplicação?

Sim

Sua aplicação contém funcionalidades da linguagem que não foram mostradas no CJRM até aqui?

Não

Link do repositório da aplicação (ou pasta pública no Google Drive)

https://github.com/DevNeves/Quiz-Interativo

Maiores dificuldades durante a implementação

troquei apenas a cor do layout (porque eu tentei fazer um layout do zero mas não consegui, pra mim ainda é dificil usar as classes do bootstrap) e inplementei a pontuação, eu ja tinha visto o video em que o roger mostra como checar as respostas mas, no dia seguinte eu fiz sozinho sem consultar em nada.

OBS: Ainda não vi o resto dos videos sobre esse quiz. Então não sei a maneira que o Roger aplicou a pontuação. Vou esperar a análise para dar andamento no resto do quiz (com o setTimeout setInterval que são conteudos que ainda não vi)

Só tenho duvida enquanto ao refatoramento que fiz, não sei se ficou da melhor maneira possivel

Menores dificuldades durante a implementação

adição dos eventos, forEach, referenciando elementos

MivlaM commented 1 year ago

Olá @DevNeves!

Passando para dizer que a issue foi visualizada e em até 10 dias úteis a análise será feita =)

MivlaM commented 1 year ago

Olá, @DevNeves.

Parabéns pelo esforço em construir a primeira aplicação do treinamento =)

troquei apenas a cor do layout (porque eu tentei fazer um layout do zero mas não consegui, pra mim ainda é dificil usar as classes do bootstrap)

Em relação à estilização, o uso do Bootstrap (ou qualquer lib CSS) é opcional.

Veja o que fica mais confortável pra você.

Só tenho duvida enquanto ao refatoramento que fiz, não sei se ficou da melhor maneira possivel

O mais importante vc já fez, que é fazer funcionar =)

Não se preocupe muito com refatoração agora. Refatoração é uma habilidade que é elevada com a prática.

Conforme vc praticar, vc vai percebendo aos poucos o que pode ser melhorado.

E você ainda irá praticar bastante no decorrer do treinamento =)


Vou deixar abaixo algumas observações sobre sua aplicação.


Boas decisões

Uma boa decisão que você tomou foi a de deixar o input dentro da label. Isso faz com que o usuário não precise posicionar o mouse e clicar exatamente no input type radio. Basta clicar no texto da alternativa =)

Você entendeu que não era necessário manipular DOM dentro do forEach (um erro comum que algumas pessoas cometem). A ideia do forEach ali naquele momento era apenas calcular os pontos, exatamente como vc fez =)

Você inseriu uma barra com a pontuação total na mensagem dos pontos. Isso é bom porque traz mais clareza do progresso do usuário no quiz em relação a pontuação máxima.


4 espaços vs 2

Recomendo que ao invés de 4 espaços para indentação, use 2. E isso vale para qualquer linguagem (HTML, CSS, JS, etc).

Se um dia vc quiser enviar alguma sugestão ou modificação de artigo pro MDN, por exemplo, seu código terá que seguir o code-guideline deles:

E não só o MDN, mas muitas outras fontes recomendam que o código tenha 2 espaços ao invés de 4.

Seu olho irá cansar menos, pq suas linhas de código serão menos extensas, o que impacta na legibilidade e carga cognitiva na leitura.

À partir daqui, os exemplos do seu código que postarei abaixo já estarão formatados com 2 espaços, ao invés de 4.

Para configurar espaços no VSCode, dá uma olhada aqui:

Indentation - VSCode

Vc verá que nesse parágrafo, há um link para o get started de Settings do VSCode. Recomendo dar uma lida, mas não invista muito tempo nisso =)


Inputs desmarcados por padrão

Antes do usuário selecionar as alternativas, é bom que a primeira alternativa de cada pergunta esteja marcada por padrão. Para fazer isso, basta inserir o atributo checked no input type radio a ser marcado por padrão.

Isso é mais uma preferência pessoal, mas acho que incentiva o usuário a marcar a resposta que ele realmente acredita que seja a verdadeira.


Code Styling

Ao longo do código, há uma única inconsistência em relação ao espaçamento dentro do parenteses da função asnwers:

// antes
const asnwers = ( ) =>  { 
  // ...
}

Todo o resto do código não há esse espaçamento adicional, a ideia aqui é considerar que é uma boa prática manter um padrão na maneira que você escreve o código. Uma sugestão é que você escolha uma das formas e seja consistente com essa escolha no decorrer do código. Por exemplo:

// depois
const asnwers = () =>  { 
  // ...
}

Futuramente, você pode usar uma ferramenta automatizada de formatação de código, mas nesse momento da sua jornada, eu recomendo que vc formate manualmente.

Isso vai "forçar" vc a adotar um estilo de escrita de código e ser consistente com esse estilo ao implementar suas aplicações.


Erro ortográfico

Há um pequeno erro ortográfico na palavra asnwers na sua aplicação, nada muito prejudicial, mas acredito que vale a pena mencionar, pois um nome ortograficamente correto pode facilitar no entendimento e integridade da aplicação. O nome após a correção será answers e ela será ajustada nas seguintes ocasiões:

// antes
const correctAsnwers = ['B', 'A', 'B', 'A', 'A']

const asnwers = ( ) =>  {
  const userAsnwers = [
    //...
  ]

  return userAsnwers
}

const userCorrectAsnwers = event => {
  // ...

  const isACorrectAsnwers = (asnwer, i) => {
    if (asnwer === correctAsnwers[i]){
      // ...
    }
  }

  asnwers().forEach(isACorrectAsnwers)
  // ...
}
form.addEventListener('submit', userCorrectAsnwers)
// depois
const correctAnswers = ['B', 'A', 'B', 'A', 'A']

const answers = ( ) =>  {
  const userAnswers = [
    //...
  ]

  return userAnswers
}

const userCorrectAnswers = event => {
  // ...

  const isACorrectAnswers = (answer, i) => {
    if (answer === correctAnswers[i]){
      // ...
    }
  }

  answers().forEach(isACorrectAnswers)
  // ...
}
form.addEventListener('submit', userCorrectAnswers)

insertPoints

Se fizer sentido para você, a função insertPoints não precisa existir no código. A ideia aqui é que você pode economizar algumas linhas de código e simplificá-lo ao todo se você optar por fazer com que showPoints.textContent receba o conteúdo correspondente para cada situação, sem necessitar de uma função para ser invocada e que esse conteúdo seja passado como parâmetro dessa função. Ficaria assim:

// antes
const insertPoints = (text) => {
  showPoints.textContent = `${text}`
}

const userCorrectAsnwers = event => {
  // ...

  insertPoints(`Você acertou ${points}/5 perguntas!`)

  if (points === 0) {
    insertPoints(`Você acertou nenhuma das 5 perguntas )=`)
  }
}
// depois
const userCorrectAsnwers = event => {
  // ...

  showPoints.textContent = `Você acertou ${points}/5 perguntas!`

  if (points === 0) {
    showPoints.textContent = `Você acertou nenhuma das 5 perguntas )=`
  }
}

Função answers

Nessa função, há algumas observações que podem ser feitas. De início, a const userAnswers, que está sendo usada para armazenar as alternativas que o usuário escolher na aplicação, não necessariamente precisa ficar dentro de uma função, se você achar interessante, você pode mover a const userAnswers para dentro da função userCorrectAnswers e remover essa função answers. A vantagem aqui é justamente economizar algumas linhas de código e deixá-lo mais simples de entender, mesmo com uma função a menos, o funcionamento do código se aprimora. Ficaria assim:

// antes
const answers = () =>  {
  const userAnswers = [
    form.inputQuestion1.value,
    form.inputQuestion2.value,
    form.inputQuestion3.value,
    form.inputQuestion4.value,
    form.inputQuestion5.value,
  ]

  return userAnswers
}
// depois
const userCorrectAnswers = event => {
  event.preventDefault()

  // ...

  const userAsnwers = [
    form.inputQuestion1.value,
    form.inputQuestion2.value,
    form.inputQuestion3.value,
    form.inputQuestion4.value,
    form.inputQuestion5.value,
  ]

  userAsnwers.forEach(isACorrectAsnwers)

  // ...
}

Nomenclatura

Um princípio que gosto de seguir ao nomear é:

Considerando isso...

Se fizer sentido para você, a let points, a função isACorrectAnswers e a função userCorrectAnswers que você usou no código podem ter seus nomes substituidos por score, isCorrectAnswer e handleFormSubmit respectivamente. O intuito aqui é fazer com que os nomes diminuam de comprimento e ao mesmo tempo mantenham o sentido original e mais próximo em relação a seu papel no código. Ficaria assim:

// antes
const userCorrectAnswers = event => {
  event.preventDefault()

  let points = 0

  const userAnswers = [
    // ...
  ]

  const isACorrectAsnwers = (answer, i) => {
    if (answer === correctAsnwers[i]){
        points += 1
    }
  }

  userAnswers.forEach(isACorrectAnswers)

  showPoints.textContent = `Você acertou ${points}/5 perguntas!`

  if (points === 0) {
    // ...
  }
}
// depois
const handleFormSubmit = event => {
  event.preventDefault()

  let score = 0

  const userAnswers = [
    // ...
  ]

  const isCorrectAnswer = (answer, i) => {
    if (answer === correctAnswers[i]){
        score += 1
    }
  }

  userAnswers.forEach(isCorrectAnswer)

  showPoints.textContent = `Você acertou ${score}/5 perguntas!`

  if (score === 0) {
    // ...
  }
}

Função getUserScore

Feito esses ajustes, vou te mostrar duas abordagens de refactoring envolvendo a criação de algumas funções para deixarmos o código mais conciso em relação a cada trecho de seu funcionamento. O que quero que você entenda, nesse primeiro momento, é o conceito por trás delas. Sabendo disso, observe o código abaixo:

let score = 0

const isCorrectAnswer = (answer, i) => {
  if (answer === correctAnswers[i]){
    score += 1
  }
}  
userAnswers.forEach(isCorrectAnswer)

No código acima, está a lógica que você usou para calcular a pontuação. A sugestão aqui, é criarmos uma nova função específica para lidar com a pontuação, como vai ser uma função para obter a pontuação, um nome interessante para ela seria algo como getUserScore. Nessa nova função, podemos simplificar a lógica de obter os pontos ao declarar score dentro dessa função e fazer com que a função retorne score. Ficaria assim:

const getUserScore = userAnswers => {
  let score = 0

  userAnswers.forEach((answer, i) => {
    if (answer === correctAnswers[i]) {
      score += 1
    }
  })
  return score
}

Sendo assim, você pode invocar a função getUserScore() dentro da função que ela estava anteriormente, ou seja, em handleFormSubmit recebendo o array armazenado por userAnswers como argumento. Ficaria assim:

const handleFormSubmit = event => {
  // ...
  const score = getUserScore(userAnswers)
  // ...
}

Função getUserAnswers

A segunda abordagem é a seguinte. Vamos criar uma função especificamente para obter a resposta do usuário. Essa função pode se chamar getUserAnswers, nela podemos criar uma const userAnswer que vai receber uma expressão que pega o valor de cada inputQuestion do form. userAnswers vai receber um array vazio que será preenchido após percorrermos cada item dentro do array correctAnswers por meio do método forEach, e por fim, esse valor que contém as respostas do usuário e que foi armazenado em userAnswers vai ser retornado. Ficaria assim:

const getUserAnswers = () => {
  const userAnswers = []

  correctAnswers.forEach((_, index) => {
    const userAnswer = form[`inputQuestion${index + 1}`].value
    userAnswers.push(userAnswer)
  })

  return userAnswers
}

Por fim, você também pode invocar essa função dentro da função handleFormSubmit que o código funcionará normalmente =)

const handleFormSubmit = event => {
  event.preventDefault()

  const userAnswers = getUserAnswers()

  const score = getUserScore(userAnswers)

  // ...
}

Função showScore

Agora, a sugestão aqui é criarmos uma nova função responsável unicamente pela manipulação de DOM e invocarmos ela nessa função também. Atualmente, a função handleFormSubmit também é responsável pelas linhas de manipulação de DOM, e para deixar mais claro o papel de apenas passar informações para a frente, podemos criar uma função showScore que vai ser unicamente responsável pela manipulação de DOM e invocar essa função em handleFormSubmit. Assim, além de facilitar o fluxo de leitura, você separa mais as responsabilidades, agora que não tem manipulação de DOM em handleFormSubmit, essa função agora é apenas responsável por passar informações para frente, para que outras partes da aplicação executem o que precisa ser executado, como por exemplo, exibir a pontuação. Ficaria assim:

const showScore = score => {
  showPoints.textContent = `Você acertou ${score}/5 perguntas!`

  if (score === 0) {
    showPoints.textContent = `Você acertou nenhuma das 5 perguntas )=`
  }
}

const handleFormSubmit = event => {
  event.preventDefault()

  const userAnswers = getUserAnswers()

  const score = getUserScore(userAnswers)

  showScore(score)
}

Nas aulas seguintes, você verá outras formas de refatorar o código dessa aplicação.

Mais uma vez, parabéns pelo esforço =)

As observações fizeram sentido?

DevNeves commented 1 year ago

Muito obrigado aprendi bastante (=

Só não consegui entender essa parte


const getUserAnswers = () => {
  const userAnswers = []

  correctAnswers.forEach((_, index) => {
    const userAnswer = form[`inputQuestion${index + 1}`].value
    userAnswers.push(userAnswer)
  })

  return userAnswers
}

Essa const userAnswer, de onde vem esse 'inputQuestion' e porque ele recebe um index + 1 ?

MivlaM commented 1 year ago

Olá @DevNeves

const getUserAnswers = () => {
  const userAnswers = []

  correctAnswers.forEach((_, index) => {
    const userAnswer = form[`inputQuestion${index + 1}`].value
    userAnswers.push(userAnswer)
  })

  return userAnswers
}

Certo, vamos detalhar o que está acontecendo nessa função passo por passo então. Primeiramente a ideia é criar uma função para obter as respostas marcadas pelo usuário do quiz, dessa forma, você não vai mais precisar da const userAnswers armazenando cada resposta de forma enumerada, pois a função fará o papel dele e de forma dinâmica. Observe abaixo como é obtido o valor de cada input do usuário:

const userAnswers = [
  form.inputQuestion1.value,  // <-- Cada valor é obtido por meio da notação de ponto, e ela segue um padrão 1...2...3...4
  form.inputQuestion2.value,
  form.inputQuestion3.value,
  form.inputQuestion4.value,
]

Sabendo disso, criaremos uma função getUserAnswers que não precisa receber nenhum parâmetro, pois o que ela vai usar ou está dentro de seu escopo, ou vai estar num escopo global, como correctAnswers, por exemplo. Depois, podemos criar uma const userAnswers que vai armazenar um array vazio, de início, a função seria concebida da seguinte forma.

const getUserAnswers = () => {
  const userAnswers = []

}

Agora, vamos pegar a const correctAnswers que armazena as respostas corretas para cada pergunta, essa const foi declarada no início do código, ou seja, no escopo global, e com ela, nós vamos encadeá-la num forEach, você já viu anteriormente que o forEach pode receber três parametros sendo eles o primeiro, o item atual da iteração, o segundo, o index que corresponde ao item e o ultimo o array inteiro que está sendo iterado, mas nessa situação em específico, nós queremos apenas o segundo parâmetro, o index. Tendo em vista que não vamos usar o primeiro parâmetro, basta colocar um _ na posição que estaria o primeiro parâmetro e colocar o index (que é o segundo parâmetro) logo em seguida, ficaria assim:

correctAnswers.forEach((_, index) => {
  // ...
  })

Dessa forma, vamos declarar uma const userAnswers que vai pegar cada resposta do usuário de forma dinâmica, lembra que eu mostrei logo no início aqui desse comentário que cada alternativa estava sendo selecionada por meio da notação de ponto? Então, se você observar o trecho abaixo, verá que o que estava acontecendo nas quatro linhas de cada input, também está acontecendo dinamicamente em uma única linha de código. A ideia aqui, é que userAnswer vai armazenar um form que na notação de colchetes vai pegar o inputQuestion, passar pelo seu index e pegar o valor (que é a resposta marcada pelo usuário para aquele input), logo, por estar num método forEach, o javascript vai fazer a mesma coisa para cada um dos inputs, mas dessa vez, será o segundo input devido ao incremento de index + 1 para cada vez. E assim por diante, na terceira vez, ele pegará o valor do terceiro input e por fim, o último.

const userAnswer = form[`inputQuestion${index + 1}`].value // Nesse trecho, o valor de cada resposta do usuário do quiz é armazenada em userAnswer

Após isso, por meio do método push, a const userAnswers que foi declarada vazia, terá seu array preenchido com os inputs obtidos no trecho de código acima, e por fim, esse valor que contém as respostas do usuário e que foi armazenado em userAnswers vai ser retornado. Se você quiser observar isso sendo feito na prática, você pode executar um console.log(userAnswers) da seguinte forma:

const getUserAnswers = () => {
  const userAnswers = []

  correctAnswers.forEach((_, index) => {
    const userAnswer = form[`inputQuestion${index + 1}`].value
    userAnswers.push(userAnswer)
  })

  console.log(userAnswers) // Respostas marcadas no quiz aparecerão no console
  return userAnswers
}

Consegui esclarecer a sua dúvida? Se ainda assim não tiver entendido, sinta-se livre para comentar logo abaixo aqui na issue que estarei disposto a te ajudar!

DevNeves commented 1 year ago

Valeu pela ajuda.

No momento que eu fiz o comentario eu não tinha entendido direito como funcionava, mas depois fiquei vendo e revendo e conseguir compreender. Mas agora com a sua resposta me esclareceu mais ainda muito obrigado.

MivlaM commented 1 year ago

Ok! Vou fechar a issue aqui, qualquer coisa, sinta-se livre para abrir uma nova.