Guía 3: CRUD local con estado

React Native con Expo - App móvil de inventario

"Ahora la app dejará de ser solo visual y comenzará a crear, editar y eliminar datos."

Introducción Objetivo Contexto Provider App.js HomeScreen CRUD Categorías CRUD Productos ProductCard Detalle Actividad

1) Introducción

En la guía anterior agregamos navegación usando Drawer, Bottom Tabs y Stack. Ahora construiremos la parte funcional de la aplicación: el CRUD local.

CRUD significa:

Create

Crear nuevos registros.

Read

Leer o mostrar registros existentes.

Update

Editar registros existentes.

Delete

Eliminar registros que ya no se necesitan.

En esta guía el CRUD será local, es decir, los datos vivirán en memoria mientras la app está abierta. En la Guía 4 conectaremos estos procesos con una API usando fetch.

2) Objetivo de la guía

Al finalizar esta guía, la aplicación permitirá administrar categorías y productos desde la interfaz.

En esta guía aprenderás a:

  • Centralizar datos usando Context API.
  • Crear un estado global para categorías y productos.
  • Crear funciones para agregar, editar y eliminar categorías.
  • Crear funciones para agregar, editar y eliminar productos.
  • Relacionar productos con categorías.
  • Usar formularios con TextInput.
  • Validar datos antes de guardar.
  • Usar Alert para confirmar eliminaciones.

Resultado esperado

Al terminar, la app tendrá dos CRUD completamente funcionales: uno para categorías y otro para productos. Los usuarios podrán crear, editar, eliminar y ver los datos en tiempo real.

CRUD Funcionalidad Relación
Categorías Crear, listar, editar y eliminar categorías. Una categoría puede tener varios productos.
Productos Crear, listar, editar y eliminar productos. Cada producto pertenece a una categoría.

3) Crear un contexto para el inventario

Hasta este momento, los datos estaban escritos directamente dentro de algunas pantallas. Eso no es ideal, porque cada pantalla tendría su propia copia de los datos.

Para resolverlo, crearemos un contexto llamado InventoryContext. Este contexto guardará las categorías y productos en un solo lugar.

Crear la carpeta context

Dentro de src, crea una nueva carpeta llamada context.

Terminal
mkdir src/context

Ahora crea el archivo:

src/context/InventoryContext.js

Estructura nueva del proyecto

inventario-app/
├── App.js
├── src/
│ ├── components/
│ │ ├── CategoryPill.js
│ │ ├── ProductCard.js
│ │ └── StatCard.js
│ ├── context/
│ │ └── InventoryContext.js
│ ├── navigation/
│ │ ├── RootDrawer.js
│ │ ├── MainTabs.js
│ │ └── ProductsStack.js
│ └── screens/
│ ├── HomeScreen.js
│ ├── ProductsScreen.js
│ ├── ProductDetailScreen.js
│ ├── CategoriesScreen.js
│ ├── ProfileScreen.js
│ └── SettingsScreen.js
└── package.json
El contexto será como una pequeña bodega de datos compartida. Las pantallas podrán consultar y modificar categorías y productos desde allí.

4) Crear InventoryContext.js paso a paso

Abriremos el archivo src/context/InventoryContext.js y construiremos el código poco a poco.

Paso 1: Importar herramientas de React

Primero importaremos lo necesario desde React.

src/context/InventoryContext.js
import { createContext, useContext, useState } from 'react';

Estas herramientas sirven para lo siguiente:

Paso 2: Crear el contexto

Debajo de la importación, agregamos esta línea:

Crear contexto
const InventoryContext = createContext();

Este será el contenedor general de los datos del inventario.

Paso 3: Crear el Provider

Ahora crearemos un componente llamado InventoryProvider. Este componente envolverá la aplicación para compartir los datos.

Provider básico
export function InventoryProvider({ children }) {
  return (
    <InventoryContext.Provider value={{}}>
      {children}
    </InventoryContext.Provider>
  );
}

La palabra children representa todo lo que estará dentro del provider. En nuestro caso, será la navegación completa de la aplicación.

Paso 4: Crear datos iniciales de categorías

Dentro de la función InventoryProvider, antes del return, agregaremos el estado de categorías.

Estado de categorías
const [categorias, setCategorias] = useState([
  {
    id: 1,
    nombre: 'Tecnología',
    descripcion: 'Equipos electrónicos y computadoras',
  },
  {
    id: 2,
    nombre: 'Accesorios',
    descripcion: 'Complementos para computadoras y oficina',
  },
  {
    id: 3,
    nombre: 'Oficina',
    descripcion: 'Mobiliario y herramientas de trabajo',
  },
]);

Observa que ahora usamos setCategorias. Eso nos permitirá modificar el arreglo cuando agreguemos, editemos o eliminemos categorías.

Paso 5: Crear datos iniciales de productos

Debajo del estado de categorías, agregaremos el estado de productos.

Estado de productos
const [productos, setProductos] = useState([
  {
    id: 1,
    nombre: 'Laptop Dell',
    precio: 850,
    stock: 8,
    categoriaId: 1,
  },
  {
    id: 2,
    nombre: 'Mouse inalámbrico',
    precio: 18.5,
    stock: 25,
    categoriaId: 2,
  },
  {
    id: 3,
    nombre: 'Silla ejecutiva',
    precio: 95,
    stock: 4,
    categoriaId: 3,
  },
]);

Cada producto tiene un categoriaId. Ese dato indica a qué categoría pertenece.

Paso 6: Crear función para agregar categorías

Ahora construiremos la primera función del CRUD.

Función agregarCategoria
function agregarCategoria(nuevaCategoria) {
  setCategorias([...categorias, nuevaCategoria]);
}

Esta función recibe una nueva categoría y la agrega al arreglo usando el operador spread ....

¿Qué hace [...categorias, nuevaCategoria]?

Crea un nuevo arreglo copiando las categorías existentes y agregando al final la nueva categoría.

Paso 7: Crear función para actualizar categorías

Para editar una categoría, buscaremos la que tenga el mismo id y la reemplazaremos.

Función actualizarCategoria
function actualizarCategoria(categoriaActualizada) {
  const nuevasCategorias = categorias.map(categoria =>
    categoria.id === categoriaActualizada.id
      ? categoriaActualizada
      : categoria
  );

  setCategorias(nuevasCategorias);
}

Aquí se usa map porque queremos recorrer todas las categorías. Si encontramos la categoría que estamos editando, la reemplazamos. Si no, la dejamos igual.

Paso 8: Crear función para eliminar categorías

Para eliminar, usaremos filter.

Función eliminarCategoria
function eliminarCategoria(id) {
  const nuevasCategorias = categorias.filter(categoria => categoria.id !== id);

  setCategorias(nuevasCategorias);
}

Esta función crea un nuevo arreglo dejando únicamente las categorías cuyo id sea diferente al que queremos eliminar.

Paso 9: Crear funciones para productos

Ahora haremos lo mismo para los productos.

Agregar producto
function agregarProducto(nuevoProducto) {
  setProductos([...productos, nuevoProducto]);
}
Actualizar producto
function actualizarProducto(productoActualizado) {
  const nuevosProductos = productos.map(producto =>
    producto.id === productoActualizado.id
      ? productoActualizado
      : producto
  );

  setProductos(nuevosProductos);
}
Eliminar producto
function eliminarProducto(id) {
  const nuevosProductos = productos.filter(producto => producto.id !== id);

  setProductos(nuevosProductos);
}

Paso 10: Compartir datos y funciones

Dentro del value del provider colocaremos todo lo que queremos compartir.

Value del Provider
const value = {
  categorias,
  productos,
  agregarCategoria,
  actualizarCategoria,
  eliminarCategoria,
  agregarProducto,
  actualizarProducto,
  eliminarProducto,
};

Luego usamos esa constante dentro del Provider.

Provider con value
<InventoryContext.Provider value={value}>
  {children}
</InventoryContext.Provider>

Paso 11: Crear un Hook personalizado

Para no escribir useContext(InventoryContext) en cada pantalla, crearemos un Hook llamado useInventory.

Hook useInventory
export function useInventory() {
  return useContext(InventoryContext);
}

Este Hook nos permitirá obtener categorías, productos y funciones desde cualquier pantalla.

Código completo de InventoryContext.js

Después de todos los pasos anteriores, el archivo completo queda así:

5) Envolver la app con InventoryProvider

Ya creamos el contexto, pero todavía no lo hemos conectado con la aplicación. Para hacerlo, debemos modificar el archivo App.js.

Paso 1: Importar InventoryProvider

En App.js, agrega esta importación:

App.js
import { InventoryProvider } from './src/context/InventoryContext';

Paso 2: Envolver la navegación

Actualmente tenemos algo parecido a esto:

Antes
<NavigationContainer>
  <RootDrawer />
</NavigationContainer>

Ahora colocaremos InventoryProvider alrededor del contenedor de navegación.

Después
<InventoryProvider>
  <NavigationContainer>
    <RootDrawer />
  </NavigationContainer>
</InventoryProvider>

Código final de App.js

Desde este momento, cualquier pantalla dentro de la navegación podrá usar useInventory().

6) Actualizar HomeScreen para usar el contexto

En la Guía 1, HomeScreen tenía sus propios arreglos de categorías y productos. Ahora esos datos vendrán desde el contexto.

Paso 1: Quitar useState

En HomeScreen.js, elimina esta importación:

Eliminar
import { useState } from 'react';

Paso 2: Importar useInventory

Agrega esta importación:

Importar contexto
import { useInventory } from '../context/InventoryContext';

Paso 3: Obtener categorías y productos

Dentro de la función HomeScreen, agrega:

Usar contexto
const { categorias, productos } = useInventory();

Ahora la pantalla ya no necesita tener datos propios. Usará los datos compartidos por el contexto.

Paso 4: Mantener los cálculos

Estos cálculos pueden quedarse casi igual:

Cálculos
const totalCategorias = categorias.length;
const totalProductos = productos.length;
const productosBajoStock = productos.filter(producto => producto.stock <= 5).length;

Paso 5: Mantener la función para obtener categoría

También podemos conservar esta función:

Buscar nombre de categoría
function obtenerNombreCategoria(categoriaId) {
  const categoriaEncontrada = categorias.find(
    categoria => categoria.id === categoriaId
  );

  return categoriaEncontrada ? categoriaEncontrada.nombre : 'Sin categoría';
}
¿Qué logramos con este cambio?

Si más adelante agregamos un producto desde la pantalla de productos, el inicio también podrá reflejar esa información porque ambos leen del mismo contexto.

7) CRUD local de categorías

Ahora modificaremos CategoriesScreen.js. Esta pantalla permitirá crear, listar, editar y eliminar categorías.

Paso 1: Importar useState

Usaremos useState para controlar los campos del formulario.

Importación de React
import { useState } from 'react';

Paso 2: Importar componentes de React Native

Necesitaremos TextInput, Pressable y Alert.

Importaciones de React Native
import {
  Alert,
  ScrollView,
  View,
  Text,
  TextInput,
  Pressable,
  StyleSheet,
} from 'react-native';

Paso 3: Importar el contexto

Desde el contexto obtendremos categorías y funciones del CRUD.

Importar useInventory
import { useInventory } from '../context/InventoryContext';

Paso 4: Obtener datos y funciones

Dentro de la función CategoriesScreen, escribimos:

Usar contexto
const {
  categorias,
  productos,
  agregarCategoria,
  actualizarCategoria,
  eliminarCategoria,
} = useInventory();

También obtenemos productos porque no deberíamos eliminar una categoría si ya tiene productos asignados.

Paso 5: Crear estados para el formulario

El formulario tendrá dos campos: nombre y descripción.

Estados del formulario
const [nombre, setNombre] = useState('');
const [descripcion, setDescripcion] = useState('');
const [categoriaEditandoId, setCategoriaEditandoId] = useState(null);

La variable categoriaEditandoId nos ayudará a saber si estamos creando una categoría nueva o editando una existente.

Paso 6: Crear función para limpiar el formulario

Después de guardar o cancelar, necesitaremos limpiar los campos.

Limpiar formulario
function limpiarFormulario() {
  setNombre('');
  setDescripcion('');
  setCategoriaEditandoId(null);
}

Paso 7: Crear función para guardar paso a paso

Paso 7.1: Validar entrada

Primero verificamos que el usuario escribió algo en el nombre:

Función con validación
function guardarCategoria() {
  if (nombre.trim() === '') {
    Alert.alert('Campo requerido', 'Escribe el nombre de la categoría.');
    return;
  }
}

Si el nombre está vacío, mostramos una alerta y salimos de la función.

Paso 7.2: Crear el objeto de categoría

Después de validar, construimos el objeto que guardaremos:

Construir objeto
  const categoria = {
    id: categoriaEditandoId ? categoriaEditandoId : Date.now(),
    nombre: nombre.trim(),
    descripcion: descripcion.trim(),
  };

Nota: Si estamos editando, usamos el ID existente. Si es nueva, Date.now() genera un ID único.

Paso 8: Decidir si agregar o actualizar

Después de crear el objeto, verificamos si estamos editando o creando:

Lógica de guardado
  if (categoriaEditandoId) {
    actualizarCategoria(categoria);
  } else {
    agregarCategoria(categoria);
  }

  limpiarFormulario();

Si categoriaEditandoId tiene valor, estamos editando. Si es nulo, es una categoría nueva. Al final, limpiamos el formulario para que el usuario pueda crear otra.

Paso 9: Crear función para editar

Cuando el usuario toque el botón editar, llenaremos el formulario con los datos actuales.

Editar categoría
function editarCategoria(categoria) {
  setNombre(categoria.nombre);
  setDescripcion(categoria.descripcion);
  setCategoriaEditandoId(categoria.id);
}

Paso 10: Crear función para eliminar

Primero verificaremos si hay productos usando esa categoría.

Verificar productos relacionados
const tieneProductos = productos.some(
  producto => producto.categoriaId === id
);

Si tiene productos, mostraremos una alerta y no eliminaremos la categoría.

Evitar eliminación
if (tieneProductos) {
  Alert.alert(
    'No se puede eliminar',
    'Esta categoría tiene productos asociados.'
  );
  return;
}

Luego pediremos confirmación.

Confirmar eliminación
Alert.alert(
  'Eliminar categoría',
  '¿Deseas eliminar esta categoría?',
  [
    {
      text: 'Cancelar',
      style: 'cancel',
    },
    {
      text: 'Eliminar',
      style: 'destructive',
      onPress: () => eliminarCategoria(id),
    },
  ]
);

Paso 11: Crear el bloque del formulario

Dentro del return, agregaremos una tarjeta para el formulario.

Tarjeta del formulario
<View style={styles.formCard}>
  <Text style={styles.formTitle}>
    {categoriaEditandoId ? 'Editar categoría' : 'Nueva categoría'}
  </Text>
</View>

Ahora agregaremos el primer campo de texto.

Campo nombre
<TextInput
  style={styles.input}
  placeholder="Nombre de la categoría"
  value={nombre}
  onChangeText={setNombre}
/>

Agregamos el segundo campo.

Campo descripción
<TextInput
  style={[styles.input, styles.textArea]}
  placeholder="Descripción"
  value={descripcion}
  onChangeText={setDescripcion}
  multiline
/>

Finalmente, agregamos el botón para guardar.

Botón guardar
<Pressable style={styles.primaryButton} onPress={guardarCategoria}>
  <Text style={styles.primaryButtonText}>
    {categoriaEditandoId ? 'Actualizar categoría' : 'Guardar categoría'}
  </Text>
</Pressable>

Cuando estemos editando, también mostraremos un botón para cancelar.

Botón cancelar
{categoriaEditandoId && (
  <Pressable style={styles.cancelButton} onPress={limpiarFormulario}>
    <Text style={styles.cancelButtonText}>Cancelar edición</Text>
  </Pressable>
)}

Paso 12: Listar categorías paso a paso

Debajo del formulario, mostraremos las categorías con su estructura básica.

Paso 12.1: Estructura básica de la lista

Recorrer categorías
{categorias.map(categoria => (
  <View key={categoria.id} style={styles.card}>
    <Text style={styles.categoryName}>{categoria.nombre}</Text>
    <Text style={styles.description}>{categoria.descripcion}</Text>
  </View>
))}

En este punto, cada categoría se muestra en una tarjeta con su nombre y descripción.

Paso 12.2: Agregar botones de acciones

Ahora agregamos botones de editar y eliminar dentro de cada tarjeta.

Botones de acción
<View style={styles.actionsRow}>
  <Pressable
    style={styles.editButton}
    onPress={() => editarCategoria(categoria)}
  >
    <Text style={styles.actionText}>Editar</Text>
  </Pressable>

  <Pressable
    style={styles.deleteButton}
    onPress={() => confirmarEliminarCategoria(categoria.id)}
  >
    <Text style={styles.actionText}>Eliminar</Text>
  </Pressable>
</View>

Código final de CategoriesScreen.js

Cómo se vería CategoriesScreen con CRUD funcionando

Después de completar todos los pasos anteriores, tu pantalla de categorías se verá así:

Nueva categoría

Tecnología

Equipos electrónicos y computadoras

Accesorios

Complementos para computadoras y oficina

En la pantalla puedes ver:

8) CRUD local de productos

Ahora modificaremos ProductsScreen.js. Esta pantalla permitirá crear, editar y eliminar productos.

Además, cada producto deberá relacionarse con una categoría existente.

Paso 1: Importar useState

Importación de React
import { useState } from 'react';

Paso 2: Importar componentes necesarios

Importaciones de React Native
import {
  Alert,
  ScrollView,
  View,
  Text,
  TextInput,
  Pressable,
  StyleSheet,
} from 'react-native';

Paso 3: Importar el contexto y ProductCard

Importaciones propias
import ProductCard from '../components/ProductCard';
import { useInventory } from '../context/InventoryContext';

Paso 4: Obtener datos y funciones

Dentro de ProductsScreen, agregamos:

Usar contexto
const {
  categorias,
  productos,
  agregarProducto,
  actualizarProducto,
  eliminarProducto,
} = useInventory();

Paso 5: Crear estados del formulario

El producto tendrá nombre, precio, stock y categoría.

Estados del formulario
const [nombre, setNombre] = useState('');
const [precio, setPrecio] = useState('');
const [stock, setStock] = useState('');
const [categoriaId, setCategoriaId] = useState(null);
const [productoEditandoId, setProductoEditandoId] = useState(null);

Usamos categoriaId para guardar la categoría seleccionada.

Paso 6: Seleccionar categoría inicial

Para evitar que la categoría esté vacía, podemos usar la primera categoría disponible.

Categoría seleccionada
const categoriaSeleccionadaId = categoriaId || categorias[0]?.id;

El signo ?. evita errores si todavía no hay categorías creadas.

Paso 7: Limpiar formulario

Limpiar formulario
function limpiarFormulario() {
  setNombre('');
  setPrecio('');
  setStock('');
  setCategoriaId(null);
  setProductoEditandoId(null);
}

Paso 8: Validar formulario paso a paso

Paso 8.1: Validar que el nombre no esté vacío

Validar nombre
if (nombre.trim() === '') {
  Alert.alert('Campo requerido', 'Escribe el nombre del producto.');
  return;
}

Paso 8.2: Convertir precio y stock a números

Los inputs de texto necesitan ser convertidos a números:

Convertir a números
const precioNumero = parseFloat(precio);
const stockNumero = parseInt(stock, 10);

Paso 8.3: Validar que precio y stock sean válidos

Verificamos que sean números reales y positivos:

Validar números
if (isNaN(precioNumero) || precioNumero <= 0) {
  Alert.alert('Precio inválido', 'Escribe un precio válido.');
  return;
}

if (isNaN(stockNumero) || stockNumero < 0) {
  Alert.alert('Stock inválido', 'Escribe una cantidad válida.');
  return;
}

isNaN() verifica si el valor NO es un número. Para el precio, debe ser positivo (>0). Para el stock, puede ser 0 pero no negativo.

Paso 9: Validar categoría

Un producto debe pertenecer a una categoría.

Validación de categoría
if (!categoriaSeleccionadaId) {
  Alert.alert('Categoría requerida', 'Primero debes crear una categoría.');
  return;
}

Paso 10: Crear objeto producto

Con todos los datos validados, creamos el objeto producto:

Construir objeto
const producto = {
  id: productoEditandoId ? productoEditandoId : Date.now(),
  nombre: nombre.trim(),
  precio: precioNumero,
  stock: stockNumero,
  categoriaId: categoriaSeleccionadaId,
};

Nota que usamos los números ya convertidos (precioNumero, stockNumero) en lugar de los strings originales.

Paso 11: Guardar producto (agregar o actualizar)

Similar a categorías, verificamos si estamos editando o creando:

Lógica de guardado
if (productoEditandoId) {
  actualizarProducto(producto);
} else {
  agregarProducto(producto);
}

limpiarFormulario();

Si estamos editando, actualiza. Si no, agrega como nuevo. Al final, limpia el formulario.

Paso 12: Editar producto

Esta función colocará los datos del producto en el formulario.

Función editarProducto
function editarProducto(producto) {
  setNombre(producto.nombre);
  setPrecio(String(producto.precio));
  setStock(String(producto.stock));
  setCategoriaId(producto.categoriaId);
  setProductoEditandoId(producto.id);
}

Paso 13: Eliminar producto

Para eliminar productos, pediremos confirmación.

Confirmar eliminación
function confirmarEliminarProducto(id) {
  Alert.alert(
    'Eliminar producto',
    '¿Deseas eliminar este producto?',
    [
      {
        text: 'Cancelar',
        style: 'cancel',
      },
      {
        text: 'Eliminar',
        style: 'destructive',
        onPress: () => eliminarProducto(id),
      },
    ]
  );
}

Paso 14: Obtener nombre de categoría

Función obtenerNombreCategoria
function obtenerNombreCategoria(id) {
  const categoriaEncontrada = categorias.find(
    categoria => categoria.id === id
  );

  return categoriaEncontrada ? categoriaEncontrada.nombre : 'Sin categoría';
}

Paso 15: Crear formulario de producto

Primero creamos la tarjeta del formulario.

Tarjeta del formulario
<View style={styles.formCard}>
  <Text style={styles.formTitle}>
    {productoEditandoId ? 'Editar producto' : 'Nuevo producto'}
  </Text>
</View>

Ahora agregamos el campo del nombre.

Campo nombre
<TextInput
  style={styles.input}
  placeholder="Nombre del producto"
  value={nombre}
  onChangeText={setNombre}
/>

Agregamos el campo del precio.

Campo precio
<TextInput
  style={styles.input}
  placeholder="Precio"
  value={precio}
  onChangeText={setPrecio}
  keyboardType="decimal-pad"
/>

Agregamos el campo del stock.

Campo stock
<TextInput
  style={styles.input}
  placeholder="Stock"
  value={stock}
  onChangeText={setStock}
  keyboardType="number-pad"
/>

Paso 16: Crear selector de categorías

En lugar de usar una lista desplegable, usaremos botones tipo etiqueta. Cada categoría será un botón seleccionable.

Título del selector
<Text style={styles.label}>Categoría</Text>

Ahora recorremos las categorías:

Recorrer categorías
<View style={styles.categoryRow}>
  {categorias.map(categoria => (
    <Pressable
      key={categoria.id}
      style={[
        styles.categoryChip,
        categoriaSeleccionadaId === categoria.id && styles.categoryChipActive,
      ]}
      onPress={() => setCategoriaId(categoria.id)}
    >
      <Text
        style={[
          styles.categoryChipText,
          categoriaSeleccionadaId === categoria.id && styles.categoryChipTextActive,
        ]}
      >
        {categoria.nombre}
      </Text>
    </Pressable>
  ))}
</View>

Cuando una categoría está seleccionada, se aplican estilos adicionales.

Paso 17: Botones del formulario

Botón guardar
<Pressable style={styles.primaryButton} onPress={guardarProducto}>
  <Text style={styles.primaryButtonText}>
    {productoEditandoId ? 'Actualizar producto' : 'Guardar producto'}
  </Text>
</Pressable>
Botón cancelar
{productoEditandoId && (
  <Pressable style={styles.cancelButton} onPress={limpiarFormulario}>
    <Text style={styles.cancelButtonText}>Cancelar edición</Text>
  </Pressable>
)}

Paso 18: Listar productos

Debajo del formulario mostraremos los productos.

Listado de productos
{productos.map(producto => (
  <ProductCard
    key={producto.id}
    producto={producto}
    categoria={obtenerNombreCategoria(producto.categoriaId)}
    onPress={() =>
      navigation.navigate('DetalleProducto', {
        producto: producto,
        categoria: obtenerNombreCategoria(producto.categoriaId),
      })
    }
    onEdit={() => editarProducto(producto)}
    onDelete={() => confirmarEliminarProducto(producto.id)}
  />
))}

Observa que ahora ProductCard recibirá tres acciones:

Código final de ProductsScreen.js

Cómo se vería ProductsScreen con CRUD funcionando

Después de completar todos los pasos del CRUD de productos, tu pantalla se verá así:

Nuevo producto

Productos

Laptop Dell

$850.00 - Stock: 8

Tecnología
OK

Mouse inalámbrico

$18.50 - Stock: 25

Accesorios
OK

En la pantalla puedes ver:

9) Actualizar ProductCard

El componente ProductCard ahora debe mostrar botones para editar y eliminar.

Paso 1: Recibir nuevas props

Antes teníamos algo parecido a esto:

Antes
export default function ProductCard({ producto, categoria, onPress }) {

Ahora recibiremos también onEdit y onDelete.

Después
export default function ProductCard({ producto, categoria, onPress, onEdit, onDelete }) {

Paso 2: Agregar botones dentro de la tarjeta

Debajo de la información del producto, agregaremos:

Botones de acción
<View style={styles.actionsRow}>
  <Pressable style={styles.editButton} onPress={onEdit}>
    <Text style={styles.actionText}>Editar</Text>
  </Pressable>

  <Pressable style={styles.deleteButton} onPress={onDelete}>
    <Text style={styles.actionText}>Eliminar</Text>
  </Pressable>
</View>

Código final de ProductCard.js

En esta versión, al tocar la parte superior de la tarjeta se abre el detalle. Los botones inferiores se usan para editar o eliminar.

10) Ajuste de ProductDetailScreen

La pantalla de detalle puede mantenerse igual que en la Guía 2. Sin embargo, agregaremos una pequeña validación para evitar errores si la pantalla se abre sin datos.

Paso 1: Validar parámetros

Después de recibir route, podemos agregar:

Validación
if (!route.params) {
  return (
    <View style={styles.container}>
      <Text>No se recibió información del producto.</Text>
    </View>
  );
}

Paso 2: Recibir producto y categoría

Recibir datos
const { producto, categoria } = route.params;
Nota:

En esta guía el detalle muestra la información que se envió al navegar. En la siguiente guía, cuando trabajemos con API, podremos consultar datos más actualizados.

11) Actividad práctica

Ahora es tu turno. Realiza los siguientes cambios en la aplicación.

Ejercicio 1

Agrega una nueva categoría desde la app y verifica que aparezca en la lista.

Ejercicio 2

Edita una categoría existente y cambia su descripción.

Ejercicio 3

Intenta eliminar una categoría que tenga productos relacionados. Verifica que la app muestre una alerta y no permita eliminarla.

Ejercicio 4

Crea un nuevo producto y asígnalo a una categoría existente.

Ejercicio 5

Edita el precio y el stock de un producto.

Ejercicio 6

Elimina un producto y verifica que desaparezca de la lista.

Checklist de revisión

En la siguiente guía conectaremos la aplicación con una API usando fetch. El CRUD dejará de ser solamente local y comenzará a comunicarse con endpoints reales.