Estado interactivo en React Native con useCallback

Clase 10 de 22Curso de Fundamentos de React Native

Resumen

Aprende a gestionar hábitos de forma efectiva con React Native: incrementa la racha, marca como completo y agrega nuevos hábitos usando useState, useCallback, TextInput y Pressable. Con IDs únicos, validaciones y estilos con useThemeColor, lograrás una experiencia limpia y predecible.

¿Cómo aumentar la racha con estado y callbacks?

Para actualizar un hábito al tocarlo, se crea un callback que recibe el ID del hábito y usa setItems con .map. Así se ubica el hábito seleccionado, se alterna su isComplete y se ajusta la streak sin permitir valores negativos. Es clave no modificar directamente, sino retornar nuevos objetos.

  • Define un identificador único tipo string por hábito.
  • Usa setItems(prev => prev.map(...)) para actualizar inmutablemente.
  • Si el ID no coincide, retorna el hábito original.
  • Si coincide, alterna isComplete y ajusta streak.
  • Emplea Math.max(0, ...) para evitar rachas por debajo de cero.
import React, { useCallback, useState } from 'react'; import { Pressable, Text, View } from 'react-native'; // items: { id: string; title: string; streak: number; isComplete: boolean; priority: 'low' }[] const onToggleHabit = (setItems: React.Dispatch<React.SetStateAction<any[]>>) => useCallback((id: string) => { setItems(prev => prev.map(habit => { if (habit.id !== id) return habit; const isComplete = !habit.isComplete; const streak = isComplete ? habit.streak + 1 : Math.max(0, habit.streak - 1); return { ...habit, isComplete, streak }; })); }, [setItems]);
  • Al renderizar, pasa el ID en onPress para identificar el hábito correcto.
  • Recuerda mapear todo el listado, no solo el ítem nuevo, para mantener el estado coherente.
{items.map(({ id, title, streak, isComplete, priority }) => ( <Pressable key={id} onPress={() => toggle(id)}> <Text>{title} · racha: {streak} · {isComplete ? 'completo' : 'pendiente'} · {priority}</Text> </Pressable> ))}

¿Cómo agregar nuevos hábitos con TextInput y un botón?

Se controla una entrada de texto con state para el título del hábito. Con un callback addHabit, se valida que exista título, se construye un objeto con ID único, streak en 0, isComplete en false y priority en low, se agrega a items y se limpia el campo.

  • Controla el texto con const [nuevo, setNuevo] = useState('').
  • Valida: si el título está vacío, no agregues.
  • Genera ID único con la fecha: H${Date.now()}.
  • Inicializa streak: 0, isComplete: false, priority: 'low'.
  • Limpia el state del input tras crear.
import React, { useCallback, useState } from 'react'; import { Pressable, Text, TextInput, View, StyleSheet } from 'react-native'; import { useThemeColor } from './useThemeColor'; export function Habits() { const [items, setItems] = useState<any[]>([]); const [nuevo, setNuevo] = useState(''); const colors = useThemeColor(); const addHabit = useCallback(() => { const title = nuevo.trim(); if (!title) return; // no agregar si no hay título setItems(prev => { const id = `H${Date.now()}`; const habit = { id, title, streak: 0, isComplete: false, priority: 'low' }; return [...prev, habit]; }); setNuevo(''); // limpiar el campo }, [nuevo]); return ( <View style={styles.row}> <TextInput value={nuevo} onChangeText={setNuevo} placeholder="Nuevo hábito: meditar" style={[styles.input, { borderColor: colors.border, color: colors.text }]} /> <Pressable onPress={addHabit} style={[styles.button, { borderColor: colors.border }]}> <Text style={{ color: colors.text }}>añadir</Text> </Pressable> </View> ); } const styles = StyleSheet.create({ row: { flexDirection: 'row', alignItems: 'center', gap: 8 }, input: { flex: 1, borderWidth: 1, padding: 12, borderRadius: 8 }, button: { paddingHorizontal: 16, paddingVertical: 12, borderWidth: 1, borderRadius: 8 }, });
  • Asegura que TextInput provenga de la librería de React Native y que useCallback se importe desde React.
  • Usa variables de color desde useThemeColor para background, borderColor y color.

¿Qué métricas y buenas prácticas refuerzan la implementación?

Para dar retroalimentación al usuario y mantener el rendimiento, se calcula cuántos hábitos hay en total y cuántos están completos usando un hook que memoriza el resultado. Además, al renderizar, incluye la key, el title, la streak, el isComplete, la priority y la acción de selección con el ID.

  • Memoriza el conteo de completados con un hook de memoria.
  • Mantén el mapeo completo del estado al actualizar con setItems.
  • Pasa funciones y IDs correctos en onPress.
import React, { useMemo } from 'react'; const total = items.length; const completados = useMemo(() => items.filter(h => h.isComplete).length, [items]);
  • Estructura clara: entrada controlada, botón de «añadir», lista mapeada y acción de completar con racha.
  • Mensajes dinámicos basados en isComplete para reflejar el estado del día.

¿Te gustaría ver validaciones extra o un filtro por prioridad? Deja un comentario con tus ideas y casos de uso!