da2k / curso-reactjs-ninja

915 stars 322 forks source link

M4 A108: limite na seleção dos sabores #500

Closed pamplonapaulo closed 4 years ago

pamplonapaulo commented 4 years ago

Fala mestre @fdaciuk . Tudo bem?

Primeiramente, achei maravilhosa a aula. Me dei conta que nunca havia usado (nos meus projetos) o Object.keys (ou values, ou entries). E o refactoring do filter(Boolean) achei incrível.

Aula linda e tal, mas no fim fiquei muito encucado. A gente estabelece o limite conforme o tamanho da pizza e, pra fechar, fica faltando só permitir a remoção da seleção caso o user tenha atingido o limite. E então a gente finaliza a condition com:

         if (
            checkboxesChecked(checkboxes).length === flavours &&
            e.target.checked === true
          ) {
            return
          }

O que eu estranho é que quando o user clica num sabor selecionado, isto é quando o user clica num target checked, o nosso handle leia como false. E quando clica num sabor desselecionado, isto é, um target unchecked, tenhamos então checked === true.

Caramba. Não era para ser o oposto?

O e.target.checked é true quando não está selecionado? E false quando está selecionado? Eu achava que fosse o contrário.

Tentei jogar um setTimeout para entender, ver se há algo mais acontecendo, mas não consegui ler o e.target dentro desse timeout. Fiquei estudando isso por um tempo, então resolvi vir aqui te perguntar.

Eu tô ficando maluco? kkk Ou minha dúvida faz algum sentido?

Grande abraço!

fdaciuk commented 4 years ago

Oi @pamplonapaulo! Sua dúvida é completamente válida sim :) Eu acredito que falei na aula, mas vou tentar explicar de uma forma que acho que pode ficar mais fácil de entender: quando você clica em um checkbox que não tenha seu estado controlado pelo React, o que vemos é:

Mas quando o estado de checked desse checkbox é controlado pelo React, tem uma passo a mais no meio disso tudo. O que acontece é:

Deu pra sacar até aqui?

Levando isso em consideração, quando nós temos todos os sabores já selecionados, e clicamos para que um sabor fique "unchecked" (ou e.target.checked = false), esse valor chega na nossa função, handler do evento, e pelo valor de e.target.checked nós sabemos se aquele sabor está sendo "checked" ou "unchecked", antes mesmo de fazer a mudança visual (que só acontece com a alteração do estado).

Deu pra entender como isso funciona? :)

pamplonapaulo commented 4 years ago

Fala mestre @fdaciuk !

Depois de ler a sua resposta eu ainda precisava dar uma conferida no código.

Como a gente escreve "e.target" eu achei que estivéssemos olhando diretamente no DOM, não no state.

Acabo de checar que, como você disse, é do state que estamos pegando a informação:

onChange={handleChangeCheckbox(pizza.id)}

Se fosse JS puro manipulando o DOM, então o elemento input eu esperaria que já tivesse a propriedade checked, sem qualquer delay. Faz sentido pra você ou eu to me confundindo mais? rs

Acho que preciso estudar mais os states e seus traços de async, para não ser surpreendido e ter problemas no futuro.

Obrigado mais uma vez!

Um abraço!

pamplonapaulo commented 4 years ago

Acho que continuo falando bobagem. Não é state, é apenas um objeto pizzaFlavours.

Eu não entendo como console.log(e.target.checked) retorna true quando está não checked e retorna false quando está checked. kkk

pamplonapaulo commented 4 years ago

Eu acabei de inspecionar o DOM aqui no Chrome e em nenhuma situação os inputs type='checkbox' ficam com o atributo 'checked'.

Onde essa informação é guardada? rsrsrs

fdaciuk commented 4 years ago

Oi @pamplonapaulo! Vamos lá:

Como a gente escreve "e.target" eu achei que estivéssemos olhando diretamente no DOM, não no state. Sim, estamos falando do DOM mesmo. Quando um evento é disparado, através do objeto de eventos nós temos acesso ao que aconteceu no momento em que o evento foi disparado.

Nesse caso, o e.target se refere à qual checkbox disparou o evento, e o e.target.checked é o estado que estamos tentando atribuir para esse elemento.

O estado só não é atribuído na hora porque o valor final dele vem do React (ou do JS), não diretamente do resultado de e.target.checked.

E por não ser atribuído na hora, é aí que conseguimos validar se o checkbox está tentando ser "checked" ou "unchecked", e antes disso acontecer, nós tomamos as devidas precauções :)

Deu pra entender? :)

pamplonapaulo commented 4 years ago

Mestre, eu acho que minha cabeça deu um nó por não me atentar que onChange é diferente de onClick!!

É, é isso. Mas agora eu fico analisando o comportamento diferente dessa input tag, se é que podemos chamar de input tag. Parece se comportar de uma forma BEM diferente do que estava acostumado no DOM normal sem React.

O que faz exatamente o trigger do onChange? O que mudou de fato para acionar o onChange?

Porque parece ter sido um mero clique. Mesmo se a gente devolve o display block para esse input, para olhar com os olhos o comportamento do checked, vemos que ele obedece totalmente a regra do nosso código. Ele só marca e desmarca nas condições que nós demos.

Então se o click não resulta num change, o que deu o trigger no onChange? O atributo checked nunca surge na tag no DOM, em nenhuma situação. O que está de fato changing?

A regra do código só se dá dentro da própria onChange. Está lindo, mas tentando dissecar isso eu não entendo como ele visualmente não muda para checked/unchecked e, (removendo o display: none), depois da regra na função, retorna para aquilo que programamos. Ou será que isso tudo acontece porém na velocidade da luz e não podemos enxergar? rs

O Label, ao receber o click, promove o change do Input. Certo? E o change do Input aciona a função que escrevemos, que organiza as coisas no nosso state de array checkboxes. E seria a própria função que fizemos que, em um relâmpago, anula ou valida o change? O change pode ser trigged mesmo antes da confirmação de que haverá de fato o change?

Falei muito e acho que confundi você. Grosso modo, eu não entendo o onChange do input rodar quando nada muda, só por efeito do clique na label.

"The onchange event occurs when the value of an element has been changed." https://www.w3schools.com/jsref/event_onchange.asp

Mas sinceramente não quero te prender com isso. Depois a ficha vai cair rsrs...

@fdaciuk

fdaciuk commented 4 years ago

haha! Sem crise, vamos lá :)

No caso do checkbox, os eventos de click e change fazem praticamente a mesma coisa. Acho que o que você está confundindo é que: você deve estar pensando que o evento onChange só é disparado quando o elemento muda VISUALMENTE. Mas não, o que muda é o objeto desse evento no DOM.

Lembre-se que o DOM é uma API para manipular elementos HTML em tela. O checkbox (elemento da tela) é diferente do objeto do DOM.

Quando manipulamos o DOM através da API DOM, com JS (ou com React, o resultado é o mesmo), o evento de onChange dispara quando existe a intenção de mudar o elemento da tela. Na tela não muda nada quando você clica no checkbox, apenas no objeto do DOM.

Com o objeto do DOM em mãos, ANTES DE MUDAR QUALQUER COISA NA TELA, o evento onChange dispara, pois houve a intenção da mudança do estado, de "checked" para "unchecked" (ou vice-versa).

Então, antes de mudar em tela, nós validamos a informação, e fazemos o que precisa ser feito, para só então fazer a atualização do estado do elemento em tela.

Um exemplo bem bobo: esqueça React por alguns minutos, e um checkbox em um arquivo index.html:

<input type="checkbox" />

No HTML mesmo, coloque um código JS para manipular o evento de "change" (ou "click", tanto faz) desse elemento:

const checkbox = document.querySelector('input')
checkbox.addEventListener('change', (e) => {
  alert(`O novo estado para o checkbox após essa mensagem sumir vai ser ${e.target.checked}`)
})

Perceba que o estado do checkbox não vai mudar até que o alert saia da tela. Mas veja que, quando o evento dispara, nós já temos, em e.target.checked, o novo valor do estado que esse checkbox vai receber.

Então quando o evento dispara, ainda nada é alterado em tela. Há somente uma intenção de mudança. Depois que o evento dispara, aí sim o valor do checkbox pode se alterado :)

Testa aí e me diz se assim fica mais claro de entender :)

pamplonapaulo commented 4 years ago

"Perceba que o estado do checkbox não vai mudar até que o alert saia da tela. Mas veja que, quando o evento dispara, nós já temos, em e.target.checked, o novo valor do estado que esse checkbox vai receber."

ô loko mano. Então tá bom! Agora faz todo o sentido.

Me perdoe alugar você com essa questão num domingo! kkk Eu estava realmente cismado com essa ordem das coisas. Agora entendi. Acho estranho que se dê nessa ordem, que o "e.target.checked" já nos entregue o resultado da ação antes dela ser totalmente concluída, mesmo com ação em suspense/pausada como no caso desse alert() na tela, mas soluciona o meu estranhamento. Eu tendo a crer que faria mais sentido se nos retornasse o estado prévio, antes da conclusão do que fora acionado.

Muito grato pela super aula!! :)

fdaciuk commented 4 years ago

Então, o ideal é sempre pensar no seguinte: eventos acontecem sempre quando há a intenção de alguma ação. Imagine o caso, por exemplo, de um formulário: você quer que, ao submeter um formulário, antes dele de fato ser submetido, verificar se todos os campos estão ok, para só depois fazer a submissão dos dados.

Se o evento onSubmit de um formulário disparar depois que os dados já foram submetidos, como você teria acesso aos campos, para validá-los?

Por isso, quando o evento onSubmit dispara, e você quer validar os dados, você usa o e.preventDefault(), que previne a ação padrão que esse evento faria (no caso, a submissão dos dados do form), e então faz o que quer antes da ação real acontecer de fato.

Todo evento é sempre uma intenção de algo acontecer. Se o evento disparar depois que a ação já aconteceu, não faz muito sentido ter os manipuladores de evento, concorda? :)

pamplonapaulo commented 4 years ago

Sim, tenho que concordar! Olhando dessa forma realmente faz todo sentido. Essa questão da "intenção" da ação vai me ajudar a memorizar esse aprendizado. Vou me lembrar dessa conversa pra sempre! rs

Muito grato, prof.! Valeu mesmo!! :)

fdaciuk commented 4 years ago

Show de bola meu caro! Qualquer dúvida, só avisar :D