10.6-Cómo evitar perder datos al desmontar componentes en React
Cómo evitar perder datos al desmontar componentes en React
Te explico las 4 estrategias principales con ejemplos prácticos:
⚠️ El Problema:
Cuando un componente se desmonta, todo su estado local se pierde:
function FormularioTemporal() {
const [texto, setTexto] = useState(''); // ❌ Se pierde al desmontar
const [contador, setContador] = useState(0); // ❌ Se pierde al desmontar
return <input value={texto} onChange={e => setTexto(e.target.value)} />;
}
✅ Estrategia 1: Levantar el Estado (Lifting State Up)
Mover el estado al componente padre que NO se desmonta:
ANTES (problema):
function Hijo() {
const [datoImportante, setDatoImportante] = useState(''); // ❌ Se pierde
return <div>{datoImportante}</div>;
}
function Padre() {
const [mostrarHijo, setMostrarHijo] = useState(true);
return (
<div>
<button onClick={() => setMostrarHijo(!mostrarHijo)}>
Mostrar/Ocultar
</button>
{mostrarHijo && <Hijo />} {/* Al ocultar, el estado se pierde */}
</div>
);}
DESPUÉS (solución):
jsx
function Hijo({ datoImportante, setDatoImportante }) {
// El estado ahora viene del padre como prop
return (
<input
value={datoImportante}
onChange={e => setDatoImportante(e.target.value)}
/>
);
}
function Padre() {
// El estado vive AQUÍ, donde no se desmonta
const [datoImportante, setDatoImportante] = useState(''); // ✅ Persiste
const [mostrarHijo, setMostrarHijo] = useState(true);
return (
<div>
<button onClick={() => setMostrarHijo(!mostrarHijo)}>
Mostrar/Ocultar
</button>
{/* Hijo se puede desmontar, pero el estado persiste en Padre */}
{mostrarHijo && (
<Hijo
datoImportante={datoImportante}
setDatoImportante={setDatoImportante}
/>
)}
{/* El dato sigue disponible aunque Hijo esté oculto */}
<p>Dato guardado: {datoImportante}</p>
</div>
);
}
Esto es exactamente lo que hace nuestro App.js con tasks. 🎯
✅ Estrategia 2: localStorage (persistencia en el navegador)
Guardar el estado en el almacenamiento del navegador:
jsx
function FormularioPersistente() {
// 1. Cargar estado inicial desde localStorage
const [texto, setTexto] = useState(() => {
const guardado = localStorage.getItem('miTexto');
return guardado || '';
});
// 2. Guardar en localStorage CADA VEZ que cambie
useEffect(() => {
localStorage.setItem('miTexto', texto);
}, [texto]); // Se ejecuta cada vez que 'texto' cambia
return (
<input
value={texto}
onChange={e => setTexto(e.target.value)}
placeholder="Esto se guarda aunque cierres la pestaña"
/>
);}
Versión mejorada para nuestro App.js de tareas:
function App() {
// Estado inicial desde localStorage
const [tasks, setTasks] = useState(() => {
try {
const guardado = localStorage.getItem('tareas');
return guardado ? JSON.parse(guardado) : [];
} catch {
return [];
}
});
// Auto-guardar cuando tasks cambia
useEffect(() => {
localStorage.setItem('tareas', JSON.stringify(tasks));
}, [tasks]);
// Resto del código igual...}
Ventajas:
Los datos sobreviven a recargas de página
Sobreviven a cerrar y abrir el navegador
Fácil de implementar
✅ Estrategia 3: Context API (Estado Global)
Compartir estado entre componentes sin pasar props manualmente:
jsx
// 1. Crear Contexto
const TasksContext = React.createContext();
// 2. Provider (proveedor) - El "banco de datos"
function TasksProvider({ children }) {
const [tasks, setTasks] = useState([]);
// El Provider NUNCA se desmonta (está en App)
return (
<TasksContext.Provider value={{ tasks, setTasks }}>
{children}
</TasksContext.Provider>
);
}
// 3. Consumir en cualquier componente
function TaskForm() {
const { setTasks } = useContext(TasksContext);
const [textoLocal, setTextoLocal] = useState(''); // Estado temporal local
const agregarTarea = () => {
setTasks(tareasPrev => [...tareasPrev, { texto: textoLocal }]);
setTextoLocal(''); // Limpiar input local
};
return (
<div>
<input
value={textoLocal}
onChange={e => setTextoLocal(e.target.value)}
/>
<button onClick={agregarTarea}>Agregar</button>
</div>
);
}
// 4. App principal
function App() {
return (
<TasksProvider> {/* Envuelve toda la app */}
<TaskForm /> {/* Puede montarse/desmontarse */}
<TaskList /> {/* Puede montarse/desmontarse */}
<OtroComponente /> {/* Accede al mismo estado */}
</TasksProvider>
);}
✅ Estrategia 4: useRef (para valores que no causan re-render)
Para datos que necesitas conservar pero que no afectan la UI:
jsx
function FormularioConHistorial() {
const [texto, setTexto] = useState('');
const historial = useRef([]); // ⚡️ NO se pierde al desmontar
// useRef persiste entre renders y NO desencadena re-renders
const guardarEnHistorial = () => {
historial.current.push({
texto,
fecha: new Date().toISOString()
});
console.log('Historial:', historial.current);
};
return (
<div>
<input
value={texto}
onChange={e => setTexto(e.target.value)}
/>
<button onClick={guardarEnHistorial}>Guardar</button>
</div>
);
}
Nota: useRef NO se limpia automáticamente. Persiste mientras el componente esté montado.
📊 Comparativa de métodos:
🎯 Mejor solución para nuestro App.js de tareas:
La combinación ideal sería:
jsx
function App() {
// 1. Estado en App (componente raíz que no se desmonta)
const [tasks, setTasks] = useState(() => {
// 2. Cargar desde localStorage para persistencia
const saved = localStorage.getItem('tasks');
return saved ? JSON.parse(saved) : [];
});
// 3. Auto-guardar en localStorage
useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);
// 4. Pasar a componentes hijos como props
return (
<div>
<TaskForm onAddTask={addTask} />
<TaskList tasks={tasks} />
{/* Ejemplo: componente que puede montarse/desmontarse */}
{showStats && <TaskStats tasks={tasks} />}
</div>
);
}
Con esta solución:
✅ tasks vive en App (nunca se desmonta)
✅ Se guarda en localStorage (sobrevive a recargas)
✅ Los componentes hijos pueden montarse/desmontarse libremente
✅ Los datos siempre están disponibles
🚨 Error común que evitar:
jsx
// ❌ MAL: Estado en componente que se desmonta
function PopupTemporal() {
const [datosImportantes, setDatosImportantes] = useState({});
// Cuando el popup se cierre, ¡adiós datos!
return <div>...</div>;
}
// ✅ BIEN:
// 1. Estado en componente superior
// 2. O guardar antes de desmontar
function PopupTemporal({ datos, onSave, onClose }) {
const [datosLocales, setDatosLocales] = useState(datos);
const cerrar = () => {
onSave(datosLocales); // Guardar antes de cerrar
onClose();
};
return (
<div>
<input value={datosLocales.nombre} onChange={...} />
<button onClick={cerrar}>Guardar y cerrar</button>
</div>
);
}
📌 Resumen final:
Para evitar perder datos al desmontar:
Identifica qué datos deben persistir
Sube el estado al componente padre más alto necesario
Usa localStorage para datos que deben sobrevivir a recargas
Considera Context API para estado global complejo
Guarda manualmente si el componente controla su propio ciclo de vida
En nuestro caso específico:
tasks ✅ está en App (correcto)
taskText ❌ está en TaskForm (correcto, es temporal)
Si queremos persistencia: agregar localStorage
Comentarios
Publicar un comentario