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:
npx create-react-app gestor-tareas
cd gestor-tareas
npm start1.2 Limpiar archivos:
Eliminar: App.test.js, logo.svg, reportWebVitals.js, setupTests.js
1.3 Crear estructura:
src/
├── components/
│ ├── TareaList.js
│ ├── TareaForm.js
│ └── TareaItem.js
├── App.js
└── index.js📄 Paso 2: index.js
// 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)
// 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)
// 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)
// 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)
// 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)
// 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)
// 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
// 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
// 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)
// 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
// 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
// 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)
// 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):
// 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):
// 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:
Edición de tareas - Se eliminó completamente
Modo edición en TareaForm - Solo modo creación
Botón de cancelar - Ya no es necesario
Estado de edición en TareaList - Simplificado
✅ Funcionalidades mantenidas:
Crear nuevas tareas ✅
Listar todas las tareas ✅
Eliminar tareas ✅
Marcar/desmarcar como completadas ✅
CSS Grid para layout ✅
Validación de formularios ✅
🎯 Beneficios de la versión simplificada:
Menos complejidad - Ideal para principiantes
Código más limpio - Menos estados y lógica condicional
Enfoque claro - Solo operaciones CRUD básicas
Más fácil de entender - Perfecto para tutoriales introductorios
✅ Resultado final simplificado:
Estructura visual más simple:
┌──────────────────────────────────────┬──────────────────────────────────────┐ │ │ │ │ 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
Publicar un comentario