15-REACT-DATOS- Tutorial Paso a Paso SIMPLIFICADO - Gestor de Tareas (Sin Edición)


Te he simplificado el tutorial eliminando la funcionalidad de editar para mantenerlo más simple y enfocado en las operaciones básicas: crear, eliminar y marcar como completadas.

🚀 Paso 1: Configuración Inicial

1.1 Crear proyecto:

bash
npx create-react-app gestor-tareas
cd gestor-tareas
npm start

1.2 Limpiar archivos:
Eliminar: App.test.jslogo.svgreportWebVitals.jssetupTests.js

1.3 Crear estructura:

text
src/
├── components/
│   ├── TareaList.js
│   ├── TareaForm.js
│   └── TareaItem.js
├── App.js
└── index.js

📄 Paso 2: index.js

jsx
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

📦 Paso 3: App.js inicial (SIN CSS)

jsx
// App.js - SIN CSS
import React from 'react';

function App() {
  return (
    <div>
      <h1>Gestor de Tareas - En Construcción</h1>
      <p>Pronto estará listo...</p>
    </div>
  );
}

export default App;

 Verificación: Deberías ver texto plano sin estilos

📦 Paso 3.1: App.js (CON CSS básico)

jsx
// App.js - CON CSS básico
import React from 'react';

const appStyle = {
  padding: '20px',
};

const headerStyle = {
  textAlign: 'center',
  color: '#333',
};

function App() {
  return (
    <div style={appStyle}>
      <h1 style={headerStyle}>Gestor de Tareas - En Construcción</h1>
      <p style={{ textAlign: 'center', color: '#666' }}>
        Pronto estará listo...
      </p>
    </div>
  );
}

export default App;

✅ Verificación: Ahora el título está centrado con colores

📋 Paso 4: TareaList.js (SIN CSS)

jsx
// components/TareaList.js - SIN CSS
import React, { useState } from 'react';

function TareaList() {
  const [tareas, setTareas] = useState([
    { id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
    { id: 2, titulo: 'Estudiar React', descripcion: 'Hacer ejercicios', completada: true },
  ]);

  return (
    <div>
      {/* Lado izquierdo - vacío */}
      <div>
        <h2>Formulario</h2>
        <p>Aquí irá el formulario</p>
      </div>

      {/* Lado derecho - Lista */}
      <div>
        <h2>Lista de Tareas</h2>
        
        {tareas.map(tarea => (
          <div key={tarea.id}>
            <h3>{tarea.titulo}</h3>
            <p>{tarea.descripcion}</p>
            <p>Estado: {tarea.completada ? 'Completada' : 'Pendiente'}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

export default TareaList;

✅ Verificación: Todo apilado verticalmente

📋 Paso 4.1: TareaList.js (CON CSS Grid)

jsx
// components/TareaList.js - CON CSS GRID
import React, { useState } from 'react';

const containerStyle = {
  display: 'grid',
  gridTemplateColumns: '300px 1fr',  /* ¡CSS GRID! */
  gap: '20px',
};

const leftColumnStyle = {
  border: '2px solid #333',
  padding: '20px',
};

const rightColumnStyle = {
  border: '2px solid #333',
  padding: '20px',
};

function TareaList() {
  const [tareas, setTareas] = useState([
    { id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
    { id: 2, titulo: 'Estudiar React', descripcion: 'Hacer ejercicios', completada: true },
  ]);

  return (
    <div style={containerStyle}>
      {/* Columna izquierda */}
      <div style={leftColumnStyle}>
        <h2>Formulario</h2>
        <p>Aquí irá el formulario</p>
      </div>

      {/* Columna derecha */}
      <div style={rightColumnStyle}>
        <h2>Lista de Tareas</h2>
        
        {tareas.map(tarea => (
          <div key={tarea.id} style={{
            border: '1px solid #ccc',
            padding: '10px',
            marginBottom: '10px',
          }}>
            <h3>{tarea.titulo}</h3>
            <p>{tarea.descripcion}</p>
            <p>Estado: {tarea.completada ? 'Completada' : 'Pendiente'}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

export default TareaList;

✅ Verificación: ¡Ahora hay 2 columnas! Formulario a la izquierda (300px), lista a la derecha

🔄 Paso 5: Integrar TareaList en App.js (SIN CSS adicional)

jsx
// App.js - CON TareaList (SIN CSS adicional)
import React from 'react';
import TareaList from './components/TareaList';

function App() {
  return (
    <div>
      <h1>📝 Gestor de Tareas</h1>
      <TareaList />
    </div>
  );
}

export default App;

✅ Verificación: Título + 2 columnas con CSS Grid

🔄 Paso 5.1: App.js (CON CSS mejorado)

jsx
// App.js - CON CSS mejorado
import React from 'react';
import TareaList from './components/TareaList';

const appStyle = {
  padding: '20px',
  maxWidth: '1000px',
  margin: '0 auto',
};

const headerStyle = {
  textAlign: 'center',
  color: '#333',
  marginBottom: '30px',
};

function App() {
  return (
    <div style={appStyle}>
      <h1 style={headerStyle}>📝 Gestor de Tareas</h1>
      <TareaList />
    </div>
  );
}

export default App;

✅ Verificación: Layout completo centrado

✏️ Paso 6: Crear TareaForm.js (SIN CSS) - SIMPLIFICADO

jsx
// components/TareaForm.js - SIN CSS (SIMPLIFICADO)
import React, { useState } from 'react';

function TareaForm({ onSubmit }) {
  const [formData, setFormData] = useState({
    titulo: '',
    descripcion: '',
    completada: false,
  });

  const [error, setError] = useState('');

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (!formData.titulo.trim()) {
      setError('El título es obligatorio');
      return;
    }
    
    onSubmit(formData);
    
    setFormData({
      titulo: '',
      descripcion: '',
      completada: false,
    });
    
    setError('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <h2>Nueva Tarea</h2>
      
      {error && <p>{error}</p>}
      
      <div>
        <label>Título:</label>
        <input
          name="titulo"
          value={formData.titulo}
          onChange={handleChange}
          placeholder="Título de la tarea"
        />
      </div>
      
      <div>
        <label>Descripción:</label>
        <textarea
          name="descripcion"
          value={formData.descripcion}
          onChange={handleChange}
          placeholder="Descripción"
          rows="3"
        />
      </div>
      
      <div>
        <label>
          <input
            type="checkbox"
            name="completada"
            checked={formData.completada}
            onChange={handleChange}
          />
          Completada
        </label>
      </div>
      
      <div>
        <button type="submit">
          Crear Tarea
        </button>
      </div>
    </form>
  );
}

export default TareaForm;

✏️ Paso 6.1: TareaForm.js (CON CSS) - SIMPLIFICADO

jsx
// components/TareaForm.js - CON CSS (SIMPLIFICADO)
import React, { useState } from 'react';

const formStyle = {
  border: '2px solid #333',
  padding: '20px',
};

const formGroupStyle = {
  marginBottom: '15px',
};

const labelStyle = {
  display: 'block',
  marginBottom: '5px',
};

const inputStyle = {
  width: '100%',
  padding: '8px',
};

function TareaForm({ onSubmit }) {
  const [formData, setFormData] = useState({
    titulo: '',
    descripcion: '',
    completada: false,
  });

  const [error, setError] = useState('');

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (!formData.titulo.trim()) {
      setError('El título es obligatorio');
      return;
    }
    
    onSubmit(formData);
    
    setFormData({
      titulo: '',
      descripcion: '',
      completada: false,
    });
    
    setError('');
  };

  return (
    <div style={formStyle}>
      <h2>➕ Nueva Tarea</h2>
      
      {error && <p style={{ color: 'red' }}>{error}</p>}
      
      <form onSubmit={handleSubmit}>
        <div style={formGroupStyle}>
          <label style={labelStyle}>Título:</label>
          <input
            name="titulo"
            value={formData.titulo}
            onChange={handleChange}
            placeholder="Título de la tarea"
            style={inputStyle}
          />
        </div>
        
        <div style={formGroupStyle}>
          <label style={labelStyle}>Descripción:</label>
          <textarea
            name="descripcion"
            value={formData.descripcion}
            onChange={handleChange}
            placeholder="Descripción"
            rows="3"
            style={inputStyle}
          />
        </div>
        
        <div style={formGroupStyle}>
          <label>
            <input
              type="checkbox"
              name="completada"
              checked={formData.completada}
              onChange={handleChange}
            />
            Completada
          </label>
        </div>
        
        <div style={{ marginTop: '20px' }}>
          <button type="submit" style={{ width: '100%', padding: '10px' }}>
            ➕ Crear Tarea
          </button>
        </div>
      </form>
    </div>
  );
}

export default TareaForm;

🔄 Paso 7: Actualizar TareaList.js para usar TareaForm (SIMPLIFICADO)

jsx
// components/TareaList.js - CON TareaForm básico (SIMPLIFICADO)
import React, { useState } from 'react';
import TareaForm from './TareaForm';

const containerStyle = {
  display: 'grid',
  gridTemplateColumns: '300px 1fr',
  gap: '20px',
};

const rightColumnStyle = {
  border: '2px solid #333',
  padding: '20px',
};

function TareaList() {
  const [tareas, setTareas] = useState([
    { id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
    { id: 2, titulo: 'Estudiar React', descripcion: 'Hacer ejercicios', completada: true },
  ]);

  const crearTarea = (datos) => {
    const nuevaTarea = {
      ...datos,
      id: Date.now(), // Usar timestamp para ID único
    };
    setTareas([...tareas, nuevaTarea]);
  };

  return (
    <div style={containerStyle}>
      {/* Columna izquierda: Formulario */}
      <TareaForm onSubmit={crearTarea} />
      
      {/* Columna derecha: Lista */}
      <div style={rightColumnStyle}>
        <h2>Lista de Tareas ({tareas.length})</h2>
        
        {tareas.map(tarea => (
          <div key={tarea.id} style={{
            border: '1px solid #ccc',
            padding: '10px',
            marginBottom: '10px',
          }}>
            <h3>{tarea.titulo}</h3>
            <p>{tarea.descripcion}</p>
            <p>Estado: {tarea.completada ? '✅ Completada' : '⏳ Pendiente'}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

export default TareaList;

 Verificación: Ahora el formulario funciona y puedes crear tareas

📦 Paso 8: Crear TareaItem.js (SIN CSS) - SIMPLIFICADO

jsx
// components/TareaItem.js - SIN CSS (SIMPLIFICADO)
import React from 'react';

function TareaItem({ tarea, onDelete, onToggle }) {
  return (
    <div>
      <h3>{tarea.titulo}</h3>
      <p>{tarea.descripcion}</p>
      <p>Estado: {tarea.completada ? 'Completada' : 'Pendiente'}</p>
      
      <div>
        <button onClick={() => onToggle(tarea.id, !tarea.completada)}>
          {tarea.completada ? 'Desmarcar' : 'Completar'}
        </button>
        <button onClick={() => onDelete(tarea.id)}>Eliminar</button>
      </div>
    </div>
  );
}

export default TareaItem;

📦 Paso 8.1: TareaItem.js (CON CSS Grid) - SIMPLIFICADO

jsx
// components/TareaItem.js - CON CSS GRID (SIMPLIFICADO)
import React from 'react';

const taskStyle = {
  border: '1px solid #ccc',
  padding: '15px',
  marginBottom: '10px',
};

const actionsStyle = {
  display: 'grid',
  gridTemplateColumns: '1fr 1fr',  /* CSS Grid: 2 columnas iguales */
  gap: '10px',
  marginTop: '10px',
};

function TareaItem({ tarea, onDelete, onToggle }) {
  return (
    <div style={taskStyle}>
      <h3>{tarea.titulo}</h3>
      <p>{tarea.descripcion}</p>
      <p>Estado: {tarea.completada ? '✅ Completada' : '⏳ Pendiente'}</p>
      
      <div style={actionsStyle}>
        <button onClick={() => onToggle(tarea.id, !tarea.completada)}>
          {tarea.completada ? '↩️ Desmarcar' : '✅ Completar'}
        </button>
        <button onClick={() => onDelete(tarea.id)}>🗑️ Eliminar</button>
      </div>
    </div>
  );
}

export default TareaItem;

🔄 Paso 9: Actualizar TareaList.js para usar TareaItem (SIMPLIFICADO)

jsx
// components/TareaList.js - CON TareaItem (SIMPLIFICADO)
import React, { useState } from 'react';
import TareaForm from './TareaForm';
import TareaItem from './TareaItem';

const containerStyle = {
  display: 'grid',
  gridTemplateColumns: '300px 1fr',
  gap: '20px',
};

const rightColumnStyle = {
  border: '2px solid #333',
  padding: '20px',
};

function TareaList() {
  const [tareas, setTareas] = useState([
    { id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
    { id: 2, titulo: 'Estudiar React', descripcion: 'Hacer ejercicios', completada: true },
  ]);

  const crearTarea = (datos) => {
    const nuevaTarea = {
      ...datos,
      id: Date.now(), // ID único basado en timestamp
    };
    setTareas([...tareas, nuevaTarea]);
  };

  const eliminarTarea = (id) => {
    if (window.confirm('¿Eliminar tarea?')) {
      setTareas(tareas.filter(t => t.id !== id));
    }
  };

  const completarTarea = (id, completada) => {
    setTareas(tareas.map(t => 
      t.id === id ? { ...t, completada } : t
    ));
  };

  return (
    <div style={containerStyle}>
      {/* Columna izquierda: Formulario */}
      <TareaForm onSubmit={crearTarea} />
      
      {/* Columna derecha: Lista */}
      <div style={rightColumnStyle}>
        <h2>Lista de Tareas ({tareas.length})</h2>
        
        {tareas.length === 0 ? (
          <p>No hay tareas. ¡Crea tu primera tarea!</p>
        ) : (
          tareas.map(tarea => (
            <TareaItem
              key={tarea.id}
              tarea={tarea}
              onDelete={eliminarTarea}
              onToggle={completarTarea}
            />
          ))
        )}
      </div>
    </div>
  );
}

export default TareaList;

✅ Verificación: CRUD simplificado funcionando (Crear, Leer, Eliminar, Marcar como completada)

🎯 Paso 10: Versión Final Mejorada y Simplificada

10.1 TareaForm.js final (simplificado con mejoras):

jsx
// components/TareaForm.js - VERSIÓN FINAL SIMPLIFICADA
import React, { useState } from 'react';

const formStyle = {
  border: '2px solid #333',
  padding: '20px',
  borderRadius: '8px',
};

const formGroupStyle = {
  marginBottom: '15px',
};

const labelStyle = {
  display: 'block',
  marginBottom: '5px',
  fontWeight: 'bold',
};

const inputStyle = {
  width: '100%',
  padding: '8px',
  border: '1px solid #ccc',
  borderRadius: '4px',
};

function TareaForm({ onSubmit }) {
  const [formData, setFormData] = useState({
    titulo: '',
    descripcion: '',
    completada: false,
  });

  const [error, setError] = useState('');

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (!formData.titulo.trim()) {
      setError('El título es obligatorio');
      return;
    }
    
    onSubmit(formData);
    
    setFormData({
      titulo: '',
      descripcion: '',
      completada: false,
    });
    
    setError('');
  };

  return (
    <div style={formStyle}>
      <h2>➕ Nueva Tarea</h2>
      
      {error && <p style={{ color: 'red', backgroundColor: '#ffe6e6', padding: '8px', borderRadius: '4px' }}>{error}</p>}
      
      <form onSubmit={handleSubmit}>
        <div style={formGroupStyle}>
          <label style={labelStyle}>Título:</label>
          <input
            name="titulo"
            value={formData.titulo}
            onChange={handleChange}
            placeholder="Ej: Comprar leche"
            style={inputStyle}
          />
        </div>
        
        <div style={formGroupStyle}>
          <label style={labelStyle}>Descripción:</label>
          <textarea
            name="descripcion"
            value={formData.descripcion}
            onChange={handleChange}
            placeholder="Ej: Ir al supermercado"
            rows="3"
            style={inputStyle}
          />
        </div>
        
        <div style={formGroupStyle}>
          <label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
            <input
              type="checkbox"
              name="completada"
              checked={formData.completada}
              onChange={handleChange}
            />
            Marcar como completada
          </label>
        </div>
        
        <div style={{ marginTop: '20px' }}>
          <button 
            type="submit" 
            style={{ 
              width: '100%', 
              padding: '10px',
              backgroundColor: '#4CAF50',
              color: 'white',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer',
              fontSize: '16px',
            }}
          >
            ➕ Crear Tarea
          </button>
        </div>
      </form>
    </div>
  );
}

export default TareaForm;

10.2 TareaItem.js final (simplificado con mejoras):

jsx
// components/TareaItem.js - VERSIÓN FINAL SIMPLIFICADA
import React from 'react';

const taskStyle = {
  border: '1px solid #ddd',
  padding: '15px',
  marginBottom: '10px',
  borderRadius: '8px',
  backgroundColor: '#f9f9f9',
};

const completedTaskStyle = {
  ...taskStyle,
  backgroundColor: '#e8f5e9',
  borderColor: '#4CAF50',
};

const actionsStyle = {
  display: 'grid',
  gridTemplateColumns: '1fr 1fr',
  gap: '10px',
  marginTop: '15px',
};

const buttonStyle = {
  padding: '8px 12px',
  border: 'none',
  borderRadius: '4px',
  cursor: 'pointer',
  fontSize: '14px',
};

const completeButtonStyle = {
  ...buttonStyle,
  backgroundColor: '#4CAF50',
  color: 'white',
};

const deleteButtonStyle = {
  ...buttonStyle,
  backgroundColor: '#f44336',
  color: 'white',
};

function TareaItem({ tarea, onDelete, onToggle }) {
  const estiloTarea = tarea.completada ? completedTaskStyle : taskStyle;

  return (
    <div style={estiloTarea}>
      <h3 style={{ marginTop: 0, color: tarea.completada ? '#2E7D32' : '#333' }}>
        {tarea.titulo}
      </h3>
      <p style={{ color: '#666' }}>{tarea.descripcion}</p>
      
      <div style={{ 
        display: 'flex', 
        justifyContent: 'space-between', 
        alignItems: 'center',
        marginTop: '10px'
      }}>
        <span style={{ 
          color: tarea.completada ? '#2E7D32' : '#FF9800',
          fontWeight: 'bold'
        }}>
          {tarea.completada ? '✅ Completada' : '⏳ Pendiente'}
        </span>
        
        <div style={actionsStyle}>
          <button 
            onClick={() => onToggle(tarea.id, !tarea.completada)}
            style={completeButtonStyle}
          >
            {tarea.completada ? '↩️ Desmarcar' : '✅ Completar'}
          </button>
          <button 
            onClick={() => onDelete(tarea.id)}
            style={deleteButtonStyle}
          >
            🗑️ Eliminar
          </button>
        </div>
      </div>
    </div>
  );
}

export default TareaItem;

📊 Resumen de Cambios Realizados

❌ Funcionalidades eliminadas:

  1. Edición de tareas - Se eliminó completamente

  2. Modo edición en TareaForm - Solo modo creación

  3. Botón de cancelar - Ya no es necesario

  4. Estado de edición en TareaList - Simplificado

✅ Funcionalidades mantenidas:

  1. Crear nuevas tareas ✅

  2. Listar todas las tareas ✅

  3. Eliminar tareas ✅

  4. Marcar/desmarcar como completadas ✅

  5. CSS Grid para layout ✅

  6. Validación de formularios ✅

🎯 Beneficios de la versión simplificada:

  1. Menos complejidad - Ideal para principiantes

  2. Código más limpio - Menos estados y lógica condicional

  3. Enfoque claro - Solo operaciones CRUD básicas

  4. Más fácil de entender - Perfecto para tutoriales introductorios

✅ Resultado final simplificado:

Estructura visual más simple:

text
┌──────────────────────────────────────┬──────────────────────────────────────┐
│                                      │                                      │
│  FORMULARIO CREAR TAREA              │  LISTA DE TAREAS                    │
│                                      │                                      │
│  [Título]       _____________        │  • Comprar leche                    │
│                 [Ir al super]        │    Ir al supermercado               │
│                                      │    Estado: ⏳ Pendiente              │
│  [Descripción]  _____________        │    [✅ Completar] [🗑️ Eliminar]     │
│                 [___________]        │                                      │
│                                      │  • Estudiar React                    │
│  [☑ Completada]                     │    Hacer ejercicios                  │
│                                      │    Estado: ✅ Completada            │
│  [➕ Crear Tarea]                    │    [↩️ Desmarcar] [🗑️ Eliminar]     │
│                                      │                                      │
└──────────────────────────────────────┴──────────────────────────────────────┘

¡Perfecto! Ahora los estudiantes tienen una versión más simple y enfocada en los conceptos básicos de React, useState, props y CSS Grid, sin la complejidad adicional de la funcionalidad de edición.

Comentarios

Entradas más populares de este blog

0-Sistema de Tareas

13-CRUD en Laravel para Web y API con Rutas Normales (No Resource)

10-Introducción a Blade en Laravel con un Ejemplo Práctico