/egghead_begginers_guide_react

Curso feito no EggHead: https://egghead.io/lessons/react-create-html-elements-with-react-s-createelement-api

Primary LanguageHTML

link do curso

EggHead the Begginer's Guide to React

Aula 00: Introdução

Estratégia do Curso: Começar com um html simples e ir divagarzin adicionando conceitos e coisas do React. Como o Reat.createElement funciona; como o JSX é só uma abstração em cima do React e como o babel funciona para fazer o JSX funcionar como se fosse parte do próprio JS.

A ideia é mostrar que React é nada além de javascript, apenas objetos e funções.

Aula 01: Create HTML elements with React's createElement API

  <div id="root"></div>
  <script type = "text/javascript">
    const rootElement = document.getElementById('root')
    const element = document.createElement('div')
    element.textContent = 'Hello World'
    element.className = 'container'
    rootElement.appendChild(element)
  </script>

com react é bem similar, vamos ter um createElement e vamos renderizar ele (não com o appendChild e sim com um método do react)

Primeiramente, precisamos colocar o react:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

O código acima em React seria dessa forma:

  const rootElement = document.getElementById('root')
  const element = React.createElement('div', {className: 'container'}, 'Hello World')
  ReactDOM.renbder(element, rootElement)

O createElement retora um objeto JS que tem alguns atributos. Um dos mais importantes é o props que é um merge dos parâmetros que passamos no createElement (sem contar o primeiro, lógico). Podemos passar quantos parâmetros quisermos dentro do createElement que estarão dentro do children deste props como um array

  const element = React.createElement('div', {className: 'container'}, 'Hello World', 'how are you?')

outras forams de fazer a mesma coisa seria dentro do segundo parâmetro:

  const element = React.createElement('div', {className: 'container',
  children: ['Hello World', 'how are you?']})

No fim o React.createElement é simples: primeiro o elemento que quer criar, depois as propriedades que você quer que aquele objeto tenha e por fim filhos que aquele objeto deve ter.

Aula 02: Replace React createElement Function Call with JSX

Criar toda nossa aplicação utilizando o React.createElement é possível, porém, não é muito ergonomico e de fácil entendimendo. Tendo isso em mente, o time do React criou o JSX (javascript + XML). com o intuito de criar nossa UI de uma maneira que é um pouco mais familiar para nós.

Para o JSX funcionar precisamos transpilar nosso JSX para o React.createElement. Para isso vamos utilizar babel:

  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  <!-- <script type = "text/javascript"> -->
  <script type = "text/babel">

Lembre-se de mudar o o text/javascript para text/babel

Temos algumas diferenças. Em HTML usamos class e no JSX deve ser className.

  //const element = React.createElement('div', {className: 'container'}, 'Hello World')
  const element = <div className = 'container'> Hello World </div>

Podemos também colocar variáveis, utilizando a interpolação (através de {} podendo utilizar qualquer código javascript que quisermos desde que ele se resolva a uma expressão)

  const content = 'Hello World'
  const element = <div className = 'container'> {content} </div>

Podemos até colocar funções se quisermos

  const content = 'Hello World'
  const element = <div className = 'container'> {(() => content)} </div>

O mesmo vale para as propriedades

  const content = 'Hello World'
  const myClassName = 'container'
  const element = <div className = {myClassName}> {content} </div>

Isso nos da poder e flexibilidade para fazer qualquer tipo de interpolação que quisermos, como por exemplo:

  const content = 'Hello World'
  const myClassName = 'container'
  const element = <div className = {myClassName + '__hi-there'}> {content} </div>

Isso ajuda bastante no BEM, porém, como o react tem o styled components ele supre a necessidade de fazer isso, já que o objetivo do BEM é isolar o escopo das coisas e o styled components já faz isso com os ids que ele cria

Outra coisa bem comum de fazer com JSX é passar os props para o componente criado com JSX:

  const props = {
    className: 'container',
    children: 'Hello World'
  }

  const element = <div {...props} />

Outra coisa legal é imaginar que este objeto com as propriedades está vindo para nós de algum outro lugar e queremos sobrescrevê-lo com algumas outras coisas (se não mandarem um className quero que tenha minha className por exemplo, ou mesmo que mandem quero que tenha uma minha)

  const props = {
    className: 'container',
    children: 'Hello World'
  }

  const element = <div {...props} className = 'batata' children = 'Holla Mundo'/>

Desta forma estamos sobrescrevendo tudo que estiver no props (meio que o conceito de definir a variável duas vezes e ela ficar com o último valor). A propriedade children também é alterada (podemos sobreescrever children ao fazer <div ...>{o_que_eu_quiser}<div/> também)

O jeito mais comum de trabalhar com JSX é este de colocar a tag HTML como se fosse o HTML mesmo, colocar as props com o spread operator ({...props}) e sobrescrever qualquer coisa que queremos.

Aula 03: Create a Simple Reusable React Component

Podemos utilizar a interpolação para não nos repetirmos

  const helloWorld = <div>Hello World</div>

  const element = (
    <div className="container">
      {helloWorld}
      {helloWorld}
    </div>
  )

Como é javascript podemos utilizar uma função para o mesmo:

  const message = (props) => <div>{props.msg}</div>

  const element = (
    <div className="container">
      {message({msg: 'Hello World'})}
      {message({msg: 'Goodbye World'})}
    </div>
  )

Infelizmente, funções não "combinam" tão bem quanto o JSX então vamos tentar uma forma diferente. Podemos fazer com ``React.createElement``` e funcionará:

  let message = (props) => <div>{props.msg}</div>
  let element = (
    <div className="container">
      {React.createElement(message, {msg: 'Hello World'})}
      {React.createElement(message, {msg: 'Goodbye World'})}
    </div>
  )

Porém, queremos agora utilizar como o JSX, geralmente criamos apenas uma tag com o que queremos:

  element = (
    <div className="container">
      <message />
      {React.createElement(message, {msg: 'Hello World'})}
      {React.createElement(message, {msg: 'Goodbye World'})}
    </div>
  )

Porém, ao fazer isso temos um problema. Como temos uma variável/função com o mesmo nome da tag que criamos o babel vai tentar compilar para um React.createElment e receberá as props como null e tentará criar um tag html "message". Para este caso em específico, isso é exatamente o que queremos, porém, em maioria dos casos podemos ter uma outra variável e acabar referenciando ela no momento da transpilação

Para evitar este tipo problema de referenciar alguma variável que já temos em algum lugar (sem intenção de referenciá-la) tem-se uma convenção de utilizar a primeira letra em maíusculo:

Agora temos um erro no console por não ter o Message:

Podemos então criar nosso componente:

  const Message = (props) => <div>{props.msg}</div> // mesmma coisa do message anterior
  element = (
    <div className="container">
      <Message msg ='Hello World' />
      <Message msg ='Goodbye World' />
    </div>
  )

Agora podemos reutilizar o nosso componente em qualquer outro lugar e compor qualquer tipo de componente e tal.

É uma boa prática utilizar o children nestes casos onde queremos renderizar alguma coisa:

  const Message = (props) => <div>{props.children}</div> // mesmma coisa do message anterior
  element = (
    <div className="container">
      <Message> 'Hello World' <Message/>
      <Message> 'Goodbye World' <Message/>
    </div>
  )

Aula 04: Validate Custom React Component Props with PropTypes

As vezes vamos cometer erros ao passar propriedades para nossos componentes; principalmente qunado temos outras pessoas utilizando o nosso código. Supomos que temos o código a seguir:

  function SayHello(props) {
    return (
      <div>
        Hello {props.firstName} {props.lastName} !
      </div>
    )
  }
  const element = <SayHello firstName={true} />
  ReactDOM.render(element, rootElement)

Ele não está renderizando o que queremos (está renderizando apenas Hello !)

Para avisar sobre esses erros podemos utilizar o PropTypes:

É importante notar que o protoTypes só funciona na versão de development do react, fique atento ao src do react caso esteja usando o cdn para ser o de development

    const PropTypes = {
    string(props, propName, componentName) {
      console.log('buahahahahah')
      if(typeof props[propName] !== 'string') {
        return new Error (`Hey, miguxo, você tem que passar uma string para ${propName} in ${componentName} mas você passou ${typeof props[propName]}`)
      }
    }
  } 

  SayHello.propTypes = {
    firstName: PropTypes.string,
    lastName: PropTypes.string
  }

  const element = <SayHello firstName={true} />
  ReactDOM.render(element, rootElement)

Dessa forma temos o erro impresso no log do sistema

Pelo fato desses erros serem comuns e esta tratativa padrão, o time do React criou o pacote PropTypes que implementa várias funções para nós! :D Basta importar script e remover nossa implementação do PropTypes e pronto:

<script src="https://unpkg.com/prop-types@15.6.2/prop-types.js"></script>

O único "problema" agora é que não temos mais o erro que acontecia no lastName, isso acontece devido ao propTypes assumir que nem todas as propriedades são necessárias por padrão (o que é bom quando colocamos um valor padrão). Para tornar elas necessárias basta:

  SayHello.propTypes = {
    firstName: PropTypes.string.isRequired,
    lastName: PropTypes.string.isRequired
  }

Gerando os erros:

Warning: Failed prop type: Invalid prop `firstName` of type `boolean` supplied to `SayHello`, expected `string`.
Warning: Failed prop type: The prop `lastName` is marked as required in `SayHello`, but its value is `undefined`.

Para Statefull Components (componentes que retornam ) podemos fazer da mesma maneira como acima (NomeDoComponent.propTypes = {...}) mas o mias comum é criar o propTypes como atributo estático:

  class SayHello extends React.Component {
    static propTypes = { 
      firstName: PropTypes.string,
      lastName: PropTypes.string
    }

    render () {
      console.log(this)
      console.log(PropTypes)
      const {firstName, lastName} = this.props
      return (
        <div>
          Hello {firstName} {lastName} !
        </div>
      )
    }
  }

Ao mudar para a versão de produção ele não funciona pq ele deixa as coisas mais "lentas" devido às verificações e etc. Isso é bom mas se você ainda quiser melhorar ainda mais as cosias você pode usar um plugin do babel para remover os prop-types do seu código ao compilar para produção o

Aula 05: Conditionally Render A React Component

  function Message({message}) {return <div>{message}</div>}
  const element = <Message message="Hello world" />
  ReactDOM.render(element, rootElement)

Caso tenhamos o código acima e, por algum motivo, quermeos que a mensagem seja nula ele simplesmente vai mostrar nada na tela (vai apenas criar a div). Se quisermos mostrar uma mensagem: Você não possui nenhuma mensagem podemos adicionar um condicional:

function Message({message}) {
    if(!message) {
      return <div>Ninguém te ama cara, você não tem mensagens</div>
    }
    return <div>{message}</div>
  }
  const element = <Message />
  //const element = <Message message={null}/>

  /*
    Ou então para ficar mais lindão
  */

  function Message({message}) {
    return  message 
      ? (<div>{message}</div>)
      : (<div>Ninguém te ama cara, você não tem mensagens</div>)
  }

Aula 06: Rerender a React Application

Nesta aula ele cria um appzinho com um relógio em React de uma forma que vemos como funciona a atualização do React DOM e o quanto ele é eficiente com a utilização do virtual DOM.

<script type = "text/babel">
  const rootElement = document.getElementById('root')

  const time = new Date().toLocaleString()
  const element = <div> it is {time}</div>
  ReactDOM.render(element, rootElement)

</script>

Com o código acima mostramos o hotário atual, porém, só funciona quando atualizamos a página (tem que ficar atualizando para ver ele mudar). Para fazer ele atualizar de segundo em segundo podemos englobar esso código em uma função e fazer chamar de segundo a segundo com um setInterval

  function tick() {
    const time = new Date().toLocaleString()
    const element = <div> It is <input value = {time}></input></div>
    ReactDOM.render(element, rootElement)
  }
  tick()
  setInterval(tick, 1000)

Agora ele funciona bunitin, rodando de segundo em segundo. O browser irá destacar de roxo o que está atualizando, "piscando" o horário conforme é atualizado segundo a segundo.

Se mudarmos a o time para um input e clicarmos nele pegamos o foco do input. O foco continua mesmo com o valor dele (o {time} e o foco (contorno azul do input) continua nele)

Vamos agora mudar o uso do React para o uso do JS normal

  function tick() {
    const time = new Date().toLocaleString()
    const element = `<div> It is  <input value = ${time}></input></div>`
    rootElement.innerHTML = element
  }

Dessa forma, podemos observar que o que está atualizando é o root inteiro.

Podemos ver o quanto que o React é eficiente com a sua renderização através do Virtual DOM pois ele só atualiza o que realmente mudou, em contrapartida ao elemento inteiro.

Aula 07: Style React Components with className and In Line Styles

O jeito como colocamos estilos nos componentes é uma das poucas diferenças que temos ao utilizar JSX e HTML. Ao invés de uma string o JSX recebe um objeto com as chaves das propriedades em camel case ao invés da separação por ífen e o valor da propriedade são strings:

  const element = (
    <div>
      <div style={{paddingLeft: '20px'}}> box </div>
    </div>
  )

Caso o valor seja um número JSX considera que está em pixel, podendo trocar para:

  const element = (
    <div>
      <div style={{paddingLeft: 20}}> box </div>
    </div>
  )

Outra diferença é a que já vimos com a className :

  const props = {
    className: 'box box--small',
    style: {paddingLeft: 20}
  }
  const element = (
    <div>
      <div {...props}> box </div>
    </div>
  )

Uma cilada que podemos cair sem querer é fazer uma estilização default dentro do próprio componente e logo após adicionar outra estilização; como o style é uma propriedade ele será sobrescrito caso façamos da seguinte maneira (perdendo o paddingLeft):

  function Box(props) {
    return (
      <div
        className = "box box--small"
        style = {{paddingLeft: 20}}
        {...props}
      >
      </div>
    )
  }
  const element = (
    <div>
      <Box  style = {backgroundColor: 'lightblue'}> box </Box>
    </div>
  )

Podemos fazer uma gambis para arrumar:

  function Box({style, className = '', ...rest}) {
    return (
      <div
        className = {`box ${className.trim()}`}
        style = {{paddingLeft: 20, ...style}}
        {...rest}
      >
      </div>
    )
  }
  const element = (
    <div>
      <Box  style = {{backgroundColor: 'lightblue'}} className = 'box--small'> box </Box>
      <Box  style = {{backgroundColor: 'pink'}} className = 'box--medium'> box </Box>
      <Box  style = {{backgroundColor: 'orange'}} className = 'box--large'> box </Box>
    </div>
  )

Assim temos a flexibilidade para criar as caixinhas e colocar tudo do tamanho que queremos. Porém, segundo o fessô, temos que tirar essa responsabilidade do componente pai e deixar que o filho se reponsabilize por colocar/ajustar o tamanho das caixinhas:

  function Box({style, size, className = '', ...rest}) {
    const sizeClassName = size ? `box--${size}` : ''
    return (
      <div
        className = {`box ${sizeClassName}`}
        style = {{paddingLeft: 20, ...style}}
        {...rest}
      >
      </div>
    )
  }
  const element = (
    <div>
      <Box  style = {{backgroundColor: 'lightblue'}} size = 'small'> box </Box>
      <Box  style = {{backgroundColor: 'pink'}} size = 'medium'> box </Box>
      <Box  style = {{backgroundColor: 'orange'}} size = 'large'> box </Box>
    </div>
  )

Assim fica muito mais simples para o componente pai ter o controle de como as coisas devem ser sem saber a implementação das mesmas.

Existem alguns problmeas em usar in line style e existem algumas bibliotecas que nos ajudam com esses problemas além de outros:

Aqui na Evnts usamos o Styled Components (por questão de adesão da comunidade). Acredito que independente de qual desses você utilizar irá resolver grande parte dos problemas que o BEM (block-element-modifier) preza resolver, com encapsulamento de contexto e estados. Além disso, o Styled Components também oferece funcionalidades como temas e outras coisas.

Aula08: Use Event Handlers with React

  const state = {eventCount: 0, username: ''}

  function increment() {
    setState({
      eventCount: state.eventCount + 1
    })
  }

  function updateUserName(event) {
    console.log(event)
    console.log(event.native)
    setState({
      username: event.target.value
    })
  }

  function App() {
    return (
      <div>
        <p>There have been {state.eventCount} events</p>
        <p>
          <button onClick = {increment}
          >🛎</button></p>
        <p>You typed: {state.username}</p>
        <p><input onChange={updateUserName} /></p>
      </div>
    )
  }

  function setState(newState) {
    Object.assign(state, newState)
    renderApp()
  }

  function renderApp() {
    ReactDOM.render(
      <App />,
      document.getElementById('root')
    )
  }
  renderApp()

Com o app acima podemos clicar na campainha e incrementar o número de eventos e, ao digitar no input atualizar o texto logo após o You typed. Ficou assim:

Se fizermos um console.log do evento que recebemos vemos que o React tem um Proxy dele que cuida de todos os eventos. Se quisermos acessar o evento nativo basta chamar event.nativeEvent

  console.log(event)
  console.log(event.nativeEvent)

Com este proxy o React otimiza algumas coisas para nós ao trabalhar com a delegação de eventos (existindo apenas um event handler para cada tipo dentro de todo o documento).

Se inspecionarmos o elemento vemos que ele não tem o método onClick quando comparado com outro que foi criado estáticamente:

AULA 09: Use Component State with React

Fala sobre a estratégia de criar o app como deveria ficar e depois migrar para os componentes com estado. Dessa forma fica muito mais fácil de entender o que deve ser propriedade, o que deve ficar no estado e etc. Para isso:

  1. Começamos com um componente Stateless com as informações estáticas;
  function StopWatch() {
    return (
      <div style = {{textAlign: 'center'}}>
          <label style = {{fontSize: '5em', display: 'block'}}>0ms</label>
          <button style = {buttonStyles}>Start</button>
          <button style = {buttonStyles}>Clear</button>
      </div>
    )
  }

  const element = <StopWatch/>
  1. Logo em seguida, retiramos as peças que precisam do estado em forma de propriedades mesmo (apenas para ver se está tudo renderizando corretamente e entender melhor o que deverá ficar no estado);
  function StopWatch({lapse, running}) {
    return (
      <div style = {{textAlign: 'center'}}>
          <label style = {{fontSize: '5em', display: 'block'}}>{lapse}ms</label>
          <button style = {buttonStyles}>{running ? 'Stop' : 'Start'}</button>
          <button style = {buttonStyles}>Clear</button>
      </div>
    )
  }

  const element = <StopWatch lapse={10} running = {false}/>
  1. Movemos as variáveis que estão nos props para o estado;
  2. Adicionamos as iterações (onClick) e as lógicas para atualizar o estado e etc;
  class StopWatch extends React.Component {
    state = {running: false, lapse: 0}
    handleRunClick = () =>  {
      this.setState( state => {
        if(state.running) {
          clearInterval(this.timer)
        } else {
          const startTime = Date.now() - this.state.lapse
          this.timer = setInterval( () => { this.setState({lapse: Date.now() - startTime})})
        }
        return {running: !state.running}
      })
    }

    handleClearClick = () =>  {
      clearInterval(this.timer)
      this.setState({lapse: 0, running: false})
    }

    render() {
      // const {lapse, running} = this.props
      const {lapse, running} = this.state
      return (
        <div style = {{textAlign: 'center'}}>
            <label style = {{fontSize: '5em', display: 'block'}}>{lapse}ms</label>
            <button onClick = {this.handleRunClick} style = {buttonStyles}>{this.state.running ? 'Stop' : 'Start'}</button>
            <button onClick = {this.handleClearClick} style = {buttonStyles}>Clear</button>
        </div>
      )
    }
  }
  const element = <StopWatch/>

A função setState geralmente é utilizada passando o novo estado / o que queremos mudar. Ela também pode ser utilizada recebendo uma função onde seu primeiro parâmetro é o estado atual e esta função deve retornar o objeto que será o novo estado / o que queremos atualizar nele

Outra coisa massa que ele faz é a criação de um "atributo" do componente on the fly: this.timer

BONUS: Coisas interessantes que o Wesley ensinou

Ao fazer um app React e colocar uma função dentro de um onClick, ou qualquer outra função que altera o estado de um componente, temos que vincular o contexto (this).

Podemos fazer o estado (variável running dentro dele) mudar da seguinte maneira:

    // "Método" dentro do componente
    changeButton ()  {this.setState({running: !this.state.running}, () => {console.log(this.state)})}
    <button onClick = {this.changeButton.bind(this)} style = {buttonStyles}>{running ? 'Stop' : 'Start'}</button>

Dessa forma temos que fazer o bind do contexto (this), porém, podemos utilizar uma Arrow Function na declaração do "método" do componente, assim como as Arrow functions capturam o valor de this do contexto vinculado não precisamos utilizar o bind:

    // "Método" dentro do componente
    changeButton = () =>  this.setState({running: !this.state.running}, () => {console.log(this.state)})
    <button onClick = {this.changeButton} style = {buttonStyles}>{this.state.running ? 'Stop' : 'Start'}</button>

Para mais informações sobre Arrow Functions dê uma olhadinha no MDN , povo é foda e tem exemplos ótimos para mostrar a diferença ❤️

Aula 10: Stop Memory Leaks with componentWillUnmount Lifecycle Method in React

Na aula anterior criamos um comoponente de timer. O problema que temos com o componente anteriro é que, caso a gente delete o componente acontecerá um memory leak pq o intervalo que criamos e o próprio timer em si não irão parar.

Para ilustrar isso, adicionamos um checkbox que da um hide no componente:

FAZER O CODIGO DESSE BIXIN AQUI

Se colocarmos uma callback logo após usar o setState para alterar o nosso lapse com um log do que temos no lapse vamos poder visualizar o lapse alterando.

  this.setState({lapse: Date.now() - startTime}, () => {
    console.log(this.state.lapse)
  })

Ao remover o componente do DOM ou destruir ele podemos ver que o console.log continua aparecendo (ou seja, temos um memory leak).

Graças ao nosso digníssimo amiguinho React, temos o método componentWillUnmount dentro dos componentes Statefull que nos da a oportunidade de limpar o que precisarmops antes de destruir um componente ou remover ele do DOM.

  componentWillUnmount() {
    clearInterval(this.timer)
  }

Feito isso, ao remover o componente não temos mais os logs o/

Aula 11: Use Class Components with React

Basicamente o que tinha no bonus :p

Aula 12: Manipulate the DOM with React refs

As vezes vamos precisar usar um Plugin ou biblioteca que precisa de acesso aos nós do DOM para funcionar. Outras vezes precisaremos acessar o nó do DOM diretamente para pegar o valor de alguns campos de um formulário ou por qualquer outro motivo.

Para fazer isso vamos utilizar a propriedade ref do React:

class Tilt extends React.Component {
    componentDidMount() {
      console.log(this.rootNode)
      VanillaTilt.init(this.rootNode, {
        max: 40,
        speed: 400,
        glare: true,
        'max-glare':0.5
      })
    }
    render() {
      return (
        <div ref={node => (this.rootNode = node)} className="tilt-root"> 
          <div className="tilt-child"> 
              <div {...this.props} />
          </div>
        </div>
      )
    }
  }

  const element = (
  <div className = "totally-centered">
    <Tilt>
      <div className="totally-centered"> 
        vanilla-tilt.js
      </div>
    </Tilt>
  </div>)

Se passarmos a propriedade ref para o componente Tilt o que vamos ter é a referência para a instância. Seria a mesma coisa que o this. Para pegar o nó do DOM precisamos passar o ref em uma div.

Dessa forma, colocamos o valor do nó dentro de uma variável (this.rootNode) que é utilizada para o vanilla-tilt

Aula 13: Make Basic Forms with React

Sempre que criarmos um formulário sem nada no submit vamos apenas recarregar a página ao clicar no botão. Para evitar este comportamento, colocamos um onSubmit para trabalhar com o evento gerado:

  class NameForm extends React.Component{
    handleSubmit = (event) => {
      event.preventDefault()
    }
    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          <label>
            Name:
            <input type="text"/>
          </label>
          <button type = "submit"> Submit</button>
        </form>
      )
    }
  }
  const element = <NameForm/>
  ReactDOM.render(element, document.getElementById('root'))

Se darmos um valor para o atributo namedo input conseguimos acessar o valor do input pelo evento de uma forma mais clara.

Outra forma de acessar é usar o atributo ref

            <input type="text"
              name= "username"
              ref={node => this.batatinha = node }
            />

Dessa forma podemos acessar o valor do input das seguintes maneiras:

  class NameForm extends React.Component{
    handleSubmit = (event) => {
      event.preventDefault()
      // console.log(event.target})
      console.log(event.target[0].value})
      console.log(event.target.elements.username.value})
      console.log(this.batatinha.value})
      console.log(this.state})
    }

As duas primeiras formas são apenas HTML simples, onde usamnos o evento e o target deles, podendo até memso pegar os elementos pelo próprio name através dos elements.

A propriedade ref é, definitivamente, apenas uma coisa do React que serve para acessar os objetos/componentes/nós do DOM criados no render. Ao utilizar esta propriedade deixamos as coisas um poquinho mais explicitas pois sabemos que estamos acessando o nó que queremos (está colocando dentro do objeto o valor que queremos).

Por ser mais explicito, Kent prefere utilizar o ref mas lembra que caso tenhamos um formulário bem grande usar o name funciona bem também por não precisarmos criar um monte de refs e cuidar deles

Aula 14: Make Dynamic Forms with React

Uma maneira de criar validações para um formulário é criar uma função que faz a validação e ao submeter chamar a mesma, exemplo:

  const getErrorMessage = (value) => {
    if(value.length < 3) return `Username deve ter no mínimo 3 caracteres`
    if(!value.includes('p')) return `Você precisa ter um "p" no seu username para fazer parte da psopciepdapde`
    return null
  }
  class NameForm extends React.Component{
    handleSubmit = (event) => {
      event.preventDefault()
      const valor = this.batatinha.value
      const erro = this.props.getErrorMessage(valor)
      if(erro) alert(`Deu ruim aí: ${erro}`)
      else alert(`xuxessu: ${valor}`)
    }

    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          <label>
            Name:
            <input type="text"
              ref={node => this.batatinha = node }
              name= "username"
            />
          </label>
          <button type = "submit"> Submit</button>
        </form>
      )
    }
  }
  const element = <NameForm
    getErrorMessage = {getErrorMessage}
  />
  ReactDOM.render(element, document.getElementById('root'))

Porém, este tipo de abordagem não é muito interessante pois não fornece ao usuáiro uma forma dinâmica de falar que ele está com algum problema. Para sanar isso podemos usar a propriedade onChange e fazer a validação conforme o usuário iterage com o input.

Para validar em tempo real teremos que utlizar um statefullComponent para observar as alterações e o que queremos.

Assim, basta colocar estado um atributo para formulário com erro, criar a função que ira tratar as alterações do input:

    state = {
      error: null
    }

    handleChange = (event) => {
      const {value} = event.target
      this.setState({error: this.props.getErrorMessage(value)})
    }

    render(){
      //...
      <input type="text" ref={node => this.batatinha = node } name= "username"
              onChange={this.handleChange}
            />
      //...
          <button disabled={Boolean(this.state.error)} type = "submit"> vai</button>
      //...
    }

Tudo parece funcionar, porém, como a validação é feita apenas no momento que ocorro alguma alteração, o atributo disabled inicializará com false, fazendo com que o botão de submit comece habilitado. Para evitar isso basta fazer a validação quando o componente terminar de montar:

  componentDidMount() {
    this.setState({error: this.props.getErrorMessage('')})
  }

Como a função de validação é passada como proprieadade poderíamos ter colocado ela nomomento de inicializção do estado também:

state = {
  error: this.props.getErrorMessage('')
}

dessa forma ainda evitados uma renderização a toa já que não estamos usando o setState e sim uma inicialização do moesmo

Só por sacanagem, bora colocar uma div com a mensagem de erro:

  {error && <div style ={{color: 'red'}}>{error}</div>}