margin-js | kuworking

Gatsby11tyAstro|UnoCSS|SolidJS

Diferencia en React entre montar y renderizar

Ojo, este artículo no se ha actualizado desde hace más de 12 meses

En React, por renderizado se entiende el construir los componentes y presentarlos, y por montaje se entiende la inserción de los componentes en el DOM

Y lo importante, cuando un componente ya está montado, éste puede renderizarse tantas veces como quiera

Montar es lo caro, renderizar es lo barato, y React para decidir si algo tiene/puede rerenderizarse o tiene que montarse/desmontarse utiliza la reconciliación(proceso interno donde va comparando el DOM con su virtual DOM)

La importancia de esto es relativa en el sentido que podemos confiar con que React hará lo más adecuado y seguir a lo nuestro

Pero puede haber casos donde este comportamiento pueda derivarse en errores

Esto es, cuando un rerender o un remount nos afecta a un estado de un modo que no habíamos previsto

Lo vemos en este ejemplo, vamos a poner un input para añadir texto en una lista, y el texto lo guardaremos en un estado

Haremos esto con dos listas, y pondremos también un botón para cambiar el color de las listas y otro para esconder una de estas listas

Cuando cambiemos el color haremos un rerenderizado de las listas, y cuando la escondamos haremos un desmontaje / montaje de esa lista

Qué pasará con sus estados?

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

export const App = () => {
  const [color, setColor] = useState('#f00')
  const [show, setShow] = useState(true)

  const [links, setLinks] = useState([])
  const [todo, setTodo] = useState([])

  const linksRef = useRef()
  const todoRef = useRef()

  const addLink = () => setLinks(prev => [linksRef.current.value, ...prev])
  const addTodo = () => setTodo(prev => [todoRef.current.value, ...prev])

  const ButtonTodo = ({ color }) => (
    <button style={{ background: color }} onClick={addTodo}>
      <span role="img" aria-label="emoji">
        ✏️
      </span>
    </button>
  )
  const ButtonLink = ({ color }) => (
    <button style={{ background: color }} onClick={addLink}>
      <span role="img" aria-label="emoji">
        ✏️
      </span>
    </button>
  )

  return (
    <div>
      <div>
        <div>
          show/hide TODO <button onClick={() => setShow(prev => !prev)}>CLICK</button>
        </div>
        <div>
          switch color<button onClick={() => setColor(prev => (prev === '#f00' ? '#ccc' : '#f00'))}>CLICK</button>
        </div>
      </div>
      <div>
        <span>LINKS</span>
        <input ref={linksRef} />
        <ButtonLink color={color} />
        <div>
          {links.map((el, i) => (
            <div key={`link${i}`}>{el}</div>
          ))}
        </div>
      </div>
      {show && (
        <div>
          <span>TODO</span>
          <input ref={todoRef} />
          <ButtonTodo color={color} />
          <div>
            {todo.map((el, i) => (
              <div key={`todo${i}`}>{el}</div>
            ))}
          </div>
        </div>
      )}
    </div>
  )
}

Este código funciona como esperamos, los estados se mantienen y todos somos felices

Pero y si quiero eliminar redundancia y hago un componente más abstracto, con un único estado?

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

const List = ({ name, color }) => {
  const [list, setList] = useState([])
  const listRef = useRef()

  const addItem = () => setList(prev => [listRef.current.value, ...prev])

  const Button = ({ color }) => (
    <button style={{ background: color }} onClick={addItem}>
      <span role="img" aria-label="emoji">
        ✏️
      </span>
    </button>
  )

  return (
    <div>
      <span>{name}</span>
      <input ref={listRef} />
      <Button color={color} />
      <div>
        {list.map((el, i) => (
          <div key={`item${name}${i}`}>{el}</div>
        ))}
      </div>
    </div>
  )
}

export const App = () => {
  const [color, setColor] = useState('#f00')
  const [show, setShow] = useState(true)

  return (
    <div>
      <div>
        <div>
          show/hide TODO <button onClick={() => setShow(prev => !prev)}>CLICK</button>
        </div>
        <div>
          switch color
          <button onClick={() => setColor(prev => (prev === '#f00' ? '#ccc' : '#f00'))}>CLICK</button>
        </div>
      </div>

      <List color={color} name="LINKS" />
      {show && <List color={color} name="TODO" />}
    </div>
  )
}

En principio todo debería seguir igual

Pero no

Al utilizar un mismo componente (reutilizado 2 veces, en lugar de 2 componentes independientes) cuando se monta/desmonta (al esconderlo) esto afecta a la otra instancia del componente

El ejemplo no tiene más, pero sirve para ver las implicaciones de renderizar y montar componentes