5 conceitos que você deve saber como desenvolvedor React

Dec 16 2022
Isso mudará a maneira como você codifica no React Introdução O React é uma biblioteca de front-end fácil de usar e aprender. Mas existem alguns conceitos que um desenvolvedor deve saber para escrever um código eficiente e de alto desempenho.

Isso mudará a maneira como você codifica no React

Foto de Lautaro Andreani no Unsplash

Introdução

React é uma biblioteca front-end fácil de usar e aprender. Mas existem alguns conceitos que um desenvolvedor deve saber para escrever um código eficiente e de alto desempenho.

Eu compilei algumas dicas e conceitos de como o estado e os efeitos funcionam no React. Tenho certeza que você aprenderá algo novo hoje!

Derivando o estado

Se houver uma variável de estado que dependa de outro estado, você pode pensar em usar a abordagem geral de usar um useEffectgancho e atualizar a variável de estado dependente com base nisso.

Vamos entender com um exemplo. Suponha que você tenha um selectelemento contendo os IDs de usuário de diferentes usuários. Você está rastreando o ID do usuário selecionado usando uma userIdvariável de estado.

import { useState } from "react"

const users = [
  { id: "1", name: "User One" },
  { id: "2", name: "User Two" },
  { id: "3", name: "User Three" },
]

function Users () {
  const [userId, setUserId] = useState("1")
  
  return(
    <select value={userId} onChange={e => setUserId(e.target.value)}>
      <option value="1">User One</option>
      <option value="2">User Two</option>
      <option value="3">User Three</option>
    </select>
  );
}

import { useState, useEffect } from "react"

function Users () {
  const [userId, setUserId] = useState("")
  const [selectedUser, setSelectedUser] = useState(undefined)
  
  useEffect(() => {
    setSelectedUser(users.find(u => u.id === userId))
  }, [userId])
  
  return(
    <>
      <select value={userId} onChange={e => setUserId(e.target.value)}>
        <option>Select a user</option>
        <option value="1">User One</option>
        <option value="2">User Two</option>
        <option value="3">User Three</option>
      </select>
      {selectedUser && <p>The selected user is: {selectedUser.name}</p>}
    </>
  );
}

A desvantagem do método acima é que ele primeiro renderiza quando userIdmuda e, em seguida, o efeito é acionado após o final da renderização porque userIdé passado em sua matriz de dependência. Os useEffectconjuntos selectedUserencontrando-o na matriz.

Isso significa que o componente renderiza duas vezes apenas para atualizar o selectedUser, na primeira vez, userIdquando useEffecto selectedUser. Vamos ver uma abordagem melhor.

function Users () {
  const [userId, setUserId] = useState("")
  const selectedUser = users.find(u => u.id === userId)
  
  return(
    <>
      <select value={userId} onChange={e => setUserId(e.target.value)}>
        <option>Select a user</option>
        <option value="1">User One</option>
        <option value="2">User Two</option>
        <option value="3">User Three</option>
      </select>
      {selectedUser && <p>The selected user is: {selectedUser.name}</p>}
    </>
  );
}

Você também poderia ter feito o seguinte se estiver usando selectedUserapenas um local e não precisar de uma variável para armazená-lo.

<p>The selected user is: {users.find(u => u.id === userId)?.name || ""}</p>

Quando há uma atualização de estado dentro dos manipuladores de eventos ou efeitos, o React renderiza novamente o componente. Mas isso não é imediato. A nova renderização ocorre somente após a chave de fechamento da função do manipulador.

function App () {
  const [name, setName] = useState("")
  const [age, setAge] = useState("")

  const handleChange = (newName, newAge) => {
    setName(newName) // batches name to be updated
    setAge(newAge) // batches age to be updated
    // other code...
    console.log(name, age) // still the old name and age
  } //  at this point, finally updates the state that was batched above and re-renders

}

Funções de limpeza

O useEffectgancho permite que você retorne uma função que é executada antes useEffectda próxima renderização e também antes de desmontar o componente. Esta função pode ser utilizada como uma forma de cancelar a assinatura dos eventos que o componente não precisa mais.

useEffect(() => {
  button.addEventListener("click", listener)

  return () => {
    button.removeEventListener("click", listener)
  }
}, [])

Isso também é útil para descartar os resultados das chamadas de API. Suponha que você faça uma chamada de API usando o userIdselecionado pelo usuário. Portanto, neste caso, você poderá descartar o resultado do arquivo userId.

Isso é importante porque se a chamada de API anterior demorasse mais do que a atual, o resultado da chamada de API anterior seria definido como o estado atual, o que não é o resultado esperado. Podemos armazenar um sinalizador dentro do efeito para combater isso.

useEffect(() => {
  let ignoreThisReq = false
  
  fetch(`/api/users/userId`).then((res) => {
    // if this is true, this effect already belongs to a previous render
    // so ignore the received data
    if(!ignoreThisReq) {
      setUser(res.data) 
    }
  })
  
  return () => {
    // clean up function is called, so discard the response from API
    ignoreThisReq = true
  }
}, [userId])

function User () {
  const [clicks, setClicks] = useState(0)
  const [clickedText, setClickedText] = useState("")
  
  useEffect(() => {
    setTimeout(() => {
      setClickedText(`Clicked ${clicks} times`)
    }, Math.random() * 5 * 1000)
  }, [clicks])
  
  return (
    <>
      <p>{clickedText}</p>
      <button onClick={() => setClicks(c => c+1)}>Click Me</button>
    </>
  )
}

useEffect(() => {
  let ignorePrev = false
  
  setTimeout(() => {
    if(!ignorePrev) {
      setClickedText(`Clicked ${clicks} times`) 
    }
  }, Math.random() * 5 * 1000)
  
  return () => {
    ignorePrev = true
  }
}, [clicks])

Há momentos em que você deseja que algumas variáveis ​​de estado sejam atualizadas ou redefinidas quando um prop é alterado. Geralmente, isso é feito usando o useEffectgancho como abaixo.

useEffect(() => {
  setSomeState(defaultValue)
}, [someProp])

A vantagem de usar essa abordagem useEffecté que ela verifica o valor prop durante a renderização e aciona uma nova renderização instantaneamente quando o estado é atualizado e os filhos não serão renderizados duas vezes.

Vamos ver a useEffectabordagem com um exemplo

import React, { useState, useEffect } from 'react'

function App () {
  const [userId, setUserId] = useState("1")
  
  return (
    <>
      <select value={userId} onChange={e => setUserId(e.target.value)}>
        <option value="1">User 1</option>
        <option value="2">User 2</option>
        <option value="3">User 3</option>
      </select>
      <User userId={userId} /> // user component
    </>
    )
}

function User ({ userId }) {
  const [clicks, setClicks] = useState(0) // record no. of clicks for current user
  
  useEffect(() => {
    setClicks(0) // reset no. of clicks when user changes
  }, [userId])
  
  console.log("User rendered")
  
  return (
    <>
      <p>No. of clicks {clicks}</p>
      <button onClick={() => setClicks(c => c+1)}>Click Me</button>
      <UserChild />
    </>
  )
}

function UserChild () {
  console.log("Child rendered")
  
  return <p>User's child</p>
}

function User ({ userId }) {
  const [clicks, setClicks] = useState(0) // record no. of clicks for current user
  const [prevUserId, setPrevUserId] = useState(user) // record previous prop

  if(userId !== prevUserId) { // this means userId prop changed
    setPrevUserId(userId)
    setClicks(0) // reset no. of clicks when user changes
  }) // component triggers a re-render at this point
  
  console.log("User rendered")
  
  return (
    <>
      <p>No. of clicks {clicks}</p>
      <button onClick={() => setClicks(c => c+1)}>Click Me</button>
      <UserChild />
    </>
  )
}

O UserChildcomponente é renderizado apenas uma vez porque uma nova renderização já foi acionada durante a renderização do Usercomponente, então ele pula a renderização de seus filhos ( UserChild) e renderiza os filhos na nova renderização.

Observe que o Usercomponente ainda será renderizado duas vezes (não os filhos).

Preservação do estado na mesma posição

O estado de um componente é preservado se for renderizado na mesma posição na árvore de IU. Isso significa que se você estiver renderizando condicionalmente o mesmo componente com props diferentes, o estado não mudará.

Vamos dar uma olhada em um exemplo.

function App () {
  const [userId, setUserId] = useState("1")
  
  return (
    <>
      <select value={userId} onChange={e => setUserId(e.target.value)}>
        <option value="1">User 1</option>
        <option value="2">User 2</option>
      </select>
      {
        userId === "1" ? 
          <User userId={userId} username="User 1" /> :
          <User userId={userId} username="User 2" />
      }
    </>
    )
}

function User ({ userId, username }) {
  const [clicks, setClicks] = useState(0)
  
  return (
    <>
      <p>No. of clicks for {username} : {clicks}</p>
      <button onClick={() => setClicks(c => c+1)}>Click Me</button>
    </>
  )
}

Embora estejamos renderizando dois componentes diferentes para usuários diferentes, eles são renderizados na mesma posição e o React pensa que é o mesmo componente e preserva o estado.

Use keypara informar ao React que esses são componentes diferentes. Isso diz ao React que este é um componente com o keyid único e o trata como um componente separado.

{
  userId === "1" ? 
    <User key={userId} userId={userId} username="User 1" /> :
    <User key={userId} userId={userId} username="User 2" />
}

{userId === "1" && <User userId={userId} username="User 1" />}
{userId === "2" && <User userId={userId} username="User 2" />}

Os conceitos acima podem não parecer significativos para pequenos aplicativos da web, mas o desempenho certamente melhorará para componentes com filhos profundamente aninhados e projetos maiores.

Obrigado por ler, até a próxima!