Context y provider para persistir hábitos en React Native

Clase 16 de 23Curso de Fundamentos de React Native

Resumen

Mantén tus hábitos incluso al cerrar y reabrir la app: con persistencia de datos, un context centralizado y un provider bien configurado, toda la UI en home y tabs accede a la misma fuente de verdad. La información vive en asset storage y se expone mediante un hook reusable, evitando duplicaciones y errores.

¿Cómo implementar persistencia con context y provider?

Para que los hábitos no se pierdan, se almacena el estado en asset storage y se entrega mediante un provider. Se retorna un ReactNode envolviendo a los children y se expone un value con el estado y las acciones. Así, al salir y volver a abrir, los datos permanecen.

  • Estado compartido: loading, habits y acciones como add habit y toggle habit.
  • Provider: entrega value y recibe children.
  • Consistencia de nombres: usa el mismo nombre del context al crear y consumir.
import React, { createContext, useContext, useState, useMemo } from 'react';

const HabitsContext = createContext(null);

export function HabitsProvider({ children }) {
  const [habits, setHabits] = useState([]);
  const [loading, setLoading] = useState(true);
  const [timer, setTimer] = useState<number>(250); // tipado de setTimer como number

  const addHabit = (id) => {
    // lógica para agregar hábito y persistir en asset storage
  };

  const toggleHabit = (id, day) => {
    // lógica para alternar completado por día y persistir
  };

  const value = useMemo(() => ({ loading, habits, addHabit, toggleHabit }), [loading, habits]);

  return (
    <HabitsContext.Provider value={value}>
      {children}
    </HabitsContext.Provider>
  );
}

¿Qué hook reusable garantiza seguridad al consumir el context?

Se exporta un hook sin parámetros, con verificación estricta: si no existe context, se hace throw con un mensaje claro. Asegúrate de retornar el ctx completo para acceder a todo.

export function useHabits() {
  const ctx = useContext(HabitsContext);
  if (!ctx) throw new Error('useHabits debe usarse dentro de HabitsProvider');
  return ctx; // retorna todo el contexto
}

¿Cómo integrar el provider en la navegación y en home?

El provider debe envolver el árbol completo donde vive la UI (por ejemplo, las tabs), igual que se hace con el theme. Así, cualquier pantalla puede leer y actualizar hábitos desde el context.

export default function App() {
  return (
    <HabitsProvider>
      <Tabs />
    </HabitsProvider>
  );
}
  • Importa desde el context: trae loading, habits y add habit con useHabits.
  • Migra lógica local: reemplaza manejadores locales por funciones del context.
  • Render: usa habits en lugar de items y elimina el key extractor externo si ya hay llaves.

¿Cómo definir onAdd con useCallback y validar entrada?

La adición de hábitos sale de la pantalla y se delega al context. Se valida el título y se limpia el estado local después.

import { useCallback, useState } from 'react';
import { useHabits } from './habits-context';

function Home() {
  const { addHabit } = useHabits();
  const [title, setTitle] = useState('');

  const onAdd = useCallback(() => {
    const id = title; // identificador por título
    if (!title) return; // validación
    addHabit(id);
    setTitle(''); // limpiar estado local
  }, [title, addHabit]);

  // ...
}

¿Cómo alternar el completado diario y mostrar loading?

Usa la función del context para alternar sin gestionar estado duplicado en home. Muestra un texto si loading es verdadero.

function Home() {
  const { loading, habits, toggleHabit } = useHabits();
  const today = new Date();

  const esMismoDia = (a, b) => a.toDateString() === b.toDateString();

  if (loading) return <Text>Cargando</Text>;

  const renderItem = ({ item }) => (
    <HabitCard
      habit={item}
      onToggle={() => toggleHabit(item.id, today)} // alterna completado hoy
    />
  );

  return (
    <FlatList
      data={habits}
      renderItem={renderItem}
      // sin key extractor externo si ya hay llaves
    />
  );
}

¿Qué ajustes garantizan consistencia y limpieza del estado?

Con la lógica centralizada, se simplifica la UI y se evitan errores comunes.

  • Filtrado de completados por día: usa habits y verifica el día actual antes de contar rachas.
  • Nombres consistentes: asegúrate de usar el mismo nombre del context al crearlo y al consumirlo.
  • Retorno de contexto: en useHabits, retorna el ctx completo para exponer todo.
  • Botón de limpieza: agrega un botón que haga un “clear” del assing storage para reiniciar hábitos.
// Ejemplo de botón para limpiar almacenamiento
<Button
  title="Limpiar"
  onPress={async () => {
    // hacer un clear del assing storage y recargar si es necesario
  }}
/>

Además, al recargar desde Expo Go verás que las tarjetas reaparecen según lo guardado; si limpias y recargas, vuelves a cero. Con estas bases ya puedes persistir hábitos, marcarlos como completados y mantener rachas sin perder datos.

¿Te gustaría conectar las sugerencias del Explorer para añadir directamente a la pantalla principal? Cuéntalo en comentarios y comparte qué más te gustaría automatizar.