Lifting state y single source of truth en React | Nicolás Garzón
TypeScript
Copiar function TemperatureCalculator ( ) {
const [ temperature, setTemperature] = useState ( "" ) ;
return (
< >
< TemperatureInput value= { temperature} onChange= { setTemperature} / >
< Preview value= { temperature} / >
< / >
) ;
} Cada dato mutable debe tener un propietario claro. No significa que todo el estado deba ser global, sino que cada pieza tenga una única fuente autoritativa.
Un componente es controlado cuando su valor importante llega por props y los cambios se comunican mediante callbacks.
TypeScript
Copiar function Toggle ( { checked, onChange } : ToggleProps) {
return (
< button onClick= { ( ) => onChange ( ! checked) } >
{ checked ? "Activo" : "Inactivo" }
< / button>
) ;
} Mantén estado local cuando ningún otro componente lo necesita. Elevarlo innecesariamente aumenta acoplamiento y renders.
Duplicar el mismo dato en padre e hijo.
Sincronizar copias con effects.
Guardar todo en Context por comodidad.
Hacer que el padre conozca detalles internos irrelevantes.
Eleva el estado para coordinar, no para centralizar todo. El propietario debe ser el ancestro común más cercano que necesita controlar el dato.
Cuando dos componentes necesitan coordinar el mismo dato, busca el ancestro común más cercano que deba tomar decisiones sobre él.
Texto
Copiar ProductPage
/ \
FilterPanel ProductResults
\ /
query stateSi FilterPanel modifica la búsqueda y ProductResults la consume, ProductPage puede poseer query y distribuir valor y callback.
TypeScript
Copiar function ProductPage ( ) {
const [ query, setQuery] = useState ( "" ) ;
return (
< >
< SearchField value= { query} onChange= { setQuery} / >
< ProductResults query= { query} / >
< / >
) ;
} Cada dato mutable necesita una autoridad clara, pero una aplicación puede tener muchas fuentes de verdad:
Un input local para su borrador.
La URL para filtros compartibles.
Una caché para datos remotos.
Un provider para configuración de una feature.
El servidor para permisos y persistencia.
La pregunta es quién tiene la última palabra para ese dato , no dónde centralizar toda la aplicación.
TypeScript
Copiar function Parent ( ) {
const [ selectedId, setSelectedId] = useState < string | null > ( null ) ;
return < Child initialSelectedId= { selectedId} / > ;
} TypeScript
Copiar function Child ( { initialSelectedId } : Props) {
const [ selectedId, setSelectedId] = useState ( initialSelectedId) ;
} Ahora existen dos valores que pueden divergir. El prefijo initial es correcto solo si el hijo crea un borrador independiente. Si deben permanecer sincronizados, el hijo debería ser controlado.
TypeScript
Copiar type AccordionProps = {
value: string | null ;
onValueChange : ( value: string | null ) => void ;
} ;
El padre coordina varias regiones.
El estado puede guardarse en URL o store.
El comportamiento es predecible desde fuera.
Más props y wiring.
El padre conoce la transición.
Puede elevar renders innecesariamente si la frontera es demasiado alta.
TypeScript
Copiar < Accordion defaultValue= "details" / > El componente administra su estado y expone configuración inicial. Es útil cuando nadie externo necesita controlar el valor.
Una API puede ofrecer ambos modos, pero debe definir claramente:
value controla.
defaultValue solo inicializa.
No se cambia de modo durante la misma instancia.
Mover cada hover, input o toggle a App crea acoplamiento y trabajo amplio.
Estado usado por una sola región.
Interacciones efímeras.
Borradores que no necesitan coordinación.
Eleva solo cuando otro componente necesita leer, modificar o persistir el dato.
TypeScript
Copiar < Layout sidebar= { < UserMenu user= { user} / > } / > Evita que niveles intermedios conozcan props que solo necesitan pasar.
Adecuado cuando muchas ramas necesitan una dependencia compartida. No elimina la necesidad de definir ownership.
Filtros, página, orden o tabs compartibles pueden vivir en search params.
Aporta cuando múltiples ramas independientes necesitan suscripciones y selectores.
TypeScript
Copiar function Toggle ( { checked, onChange } : Props) {
function handleClick ( ) {
onChange ( ! checked) ;
}
return < button onClick= { handleClick} > { checked ? "Activo" : "Inactivo" } < / button> ;
} No guardes una copia local y uses un effect para avisar después. El evento ya conoce la intención y evita un render intermedio.
TypeScript
Copiar function OrdersPage ( { orders } : { orders: Order[ ] } ) {
const [ selectedId, setSelectedId] = useState < string | null > ( null ) ;
const selectedOrder = orders. find ( ( order) => order. id === selectedId) ?? null ;
return (
< div>
< OrderList
orders= { orders}
selectedId= { selectedId}
onSelect= { setSelectedId}
/ >
< OrderDetails order= { selectedOrder} / >
< / div>
) ;
} Solo se guarda el ID. El objeto seleccionado se deriva, evitando conservar una copia que podría quedar obsoleta si cambia orders.
Elevar state amplía la región que puede renderizar. Antes de memoizar todo, revisa si el propietario está más alto de lo necesario.
Pero no dupliques state únicamente para reducir renders. Primero conserva una fuente correcta; después mide y crea fronteras mediante composición, componentes separados o stores selectivas.
Cuando dos controles se desincronizan:
Identifica cuántas copias del dato existen.
Pregunta cuál es autoritativa.
Revisa effects que copian de una a otra.
Decide si es un borrador independiente o el mismo dato.
Coloca la fuente en el ancestro común más cercano.
Una fuente de verdad clara evita sincronización manual.
Elevar state sirve para coordinar, no para globalizar.
Un componente controlado recibe valor y comunica eventos.
Un valor inicial no sigue automáticamente cambios posteriores.
URL, Context, caché y stores son propietarios diferentes según la naturaleza del dato.
¿Por qué guardar el objeto seleccionado y su ID puede ser redundante?
¿Cuándo debería un componente ser no controlado?
¿Qué señal indica que el state está demasiado alto?
¿Cómo puede composición evitar prop drilling?
Ver respuestas
Porque el objeto puede derivarse del ID y la colección, evitando una copia obsoleta.
Cuando nadie externo necesita coordinar su valor y basta una configuración inicial.
Cambios locales obligan a recalcular regiones que no necesitan el dato.
El padre puede crear el elemento final y pasarlo como slot sin que niveles intermedios conozcan sus datos.
Estado derivado y datos redundantes enseña a reducir state antes de elegir dónde colocarlo.