hand Inicio
hand JSBloqs
hand GutenBloqs

Diferencia en React entre mount y render

useReducer nos permite integrar una función en useState y así poder trabajar más cómodamente con estados complejos

React: renderizado vs montaje

  • Por renderizado se entiende el construir los componentes y presentarlos

  • 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?

jsx
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>
)
}

// el export default no es necesario si importamos `App` explícitamente (lo cual es recomendable)
// aquí lo añado aquí para que el codesandbox funcione out-of-the-box
export default App

Si has ido al codesandbox aparte de ver que no me he preocupado por la estética, también verás que puedes cambiar de color y esconder parte del código sin que sus estados se vean afectados

Hasta aquí todo normal, señal que React está rerenderizando los componentes y no los está montando y desmontando (lo que resetearía los estados)

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

jsx
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>
)
}

// el export default no es necesario si importamos `App` explícitamente (lo cual es recomendable)
// aquí lo añado aquí para que el codesandbox funcione out-of-the-box
export default App

Y aquí la cosa cambia

Puedo cambiar de color y no pierdo el estado, pero en cuanto "escondo" un componente, React lo desmonta, y cuando lo vuelvo a "enseñar" React no está haciendo un rerenderizado, lo tiene que volver a montar y allí veo que he perdido el estado

El problema viene de encapsular el estado en el mismo componente que se monta y desmonta, porqué así efectivamente estamos conservando el estado sólo cuando se rerenderiza el componente, pero no cuando se desmonta, mientras que si el estado lo tuviésemos en el componente principal nos daría igual si React renderiza o monta, el estado estaría a salvo

Otra solución sería no desmontar el componente sino esconderlo simplemente con css con lo que React no desmontará nada (lo puedes ver en el siguiente código)

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

const List = ({ name, color, show = true }) => {
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 style={{ display: show ? 'block' : 'none' }}>
<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" />
<List color={color} name="TODO" show={show} />
</div>
)
}

// el export default no es necesario si importamos `App` explícitamente (lo cual es recomendable)
// aquí lo añado aquí para que el codesandbox funcione out-of-the-box
export default App

Y ya lo tienes, diferencias entre montar y renderizar

draw of me

Hola, tienes en mente desarrollar una web?

Si quieres, te ayudo

11ty para webs ultra-rápidaseleventy js
Gatsby para webs complejasgatsby js
WordPress para webs para el usuario finalwordpress

Escríbeme

Lista de correo: escribo algo de tanto en cuanto