Introducción a React

React Logo

1. ¿Qué es React?

React es una biblioteca de JavaScript desarrollada por Facebook para construir interfaces de usuario. Se enfoca en la creación de componentes reutilizables y eficientes que permiten el desarrollo de aplicaciones web rápidas y escalables.

Características clave de React:

  • Componentes: React basa su arquitectura en componentes. Cada componente es una pieza reutilizable de código que puede manejar su propio estado y lógica.

  • Virtual DOM: React utiliza un DOM virtual para actualizar solo las partes que cambian, lo que mejora significativamente el rendimiento.

  • Unidirectional Data Flow (Flujo de Datos Unidireccional): En React, los datos fluyen en una sola dirección, desde el componente padre hacia los componentes hijos.

  • JSX: Un lenguaje similar a HTML que permite combinar la estructura del componente con lógica JavaScript.

2. Evolución de React: De create-react-app a Vite

React con create-react-app (Antes).

Antes de la aparición de herramientas como Vite, React se inicializaba principalmente usando create-react-app (CRA). CRA es un generador de proyectos que proporciona una configuración completa para desarrollar aplicaciones React sin necesidad de configuraciones manuales con Webpack o Babel.

npx create-react-app my-app
cd my-app
npm start

Con estos comandos, podías crear un proyecto React con CRA y ejecutarlo en un servidor de desarrollo. Sin embargo, CRA tiene algunas desventajas que han llevado a la adopción de herramientas más modernas como Vite.

Desventajas de create-react-app:

Lentitud: A medida que las aplicaciones crecen, CRA puede volverse lento durante el tiempo de compilación y recarga en caliente.

Sobrecarga de configuración: Aunque CRA oculta la configuración compleja, esta puede ser difícil de personalizar cuando el proyecto lo requiere.

React con Vite (Ahora)

Vite es una herramienta de desarrollo moderna que mejora significativamente los tiempos de desarrollo y la experiencia del programador, superando a CRA en términos de velocidad y simplicidad.

Ventajas de Vite:

  • Velocidad: Vite es extremadamente rápido, incluso en proyectos grandes, debido a su compilación basada en ES Modules (módulos de ECMAScript).

  • Recarga en caliente instantánea: Vite ofrece Hot Module Replacement (HMR), que recarga solo los módulos que cambian, proporcionando un entorno de desarrollo mucho más ágil.

  • Configuración ligera: La configuración es mínima y fácil de personalizar cuando es necesario.

Crear un proyecto React con Vite:

Hoy en día, los desarrolladores de React utilizan Vite para crear proyectos mucho más rápido. Aquí te mostramos cómo hacerlo.

npm create vite@latest .

Con esta sencilla línea de comandos, puedes crear un proyecto React con Vite y comenzar a desarrollar aplicaciones web modernas de forma rápida y eficiente.

Tip

Nota: Al ejecutar el comando anterior en tu terminal, se abrirá un asistente para configurar tu proyecto con Vite. Puedes elegir entre diferentes plantillas, incluyendo React, Vue, Preact y más. De la misma forma la forma en la que puedes programar ya que es posible utilizar javascrit o typescript.

npm install
npm run dev

Con los comandos anteriores, puedes instalar las dependencias y ejecutar el servidor de desarrollo de Vite para comenzar a trabajar en tu proyecto React.

3. Conceptos Básicos de React

Antes de iniciar con React es necesario conocer un poco la estructura básica de un proyecto en React, los componentes, JSX, props, estado y hooks.

Por ahora lo más importante es mantener un orden que quizás al principio no se entienda pero con el tiempo se irá aclarando.

Para ello vamos a crear una directorio llamado components y dentro del directorio src vamos a crear un archivo llamado Gretting.jsx.

Tip

JSX: Es una extensión de JavaScript que permite escribir HTML en archivos JavaScript.

function Greeting() {
  return <h1>¡Hola, mundo!</h1>;
}

export default Greeting;

3.1. Componentes en React

Los componentes son bloques reutilizables que contienen lógica, estructura y estilo. En React, un componente puede ser:

Funcional: Es el enfoque moderno en React. Son componentes escritos como funciones de JavaScript.

De clase: Son el enfoque anterior, basados en la programación orientada a objetos. Aún se usan en proyectos más antiguos.

En la sección anterior, creamos un componente funcional llamado Greeting. Ahora, vamos a importar y usar este componente en el archivo App.js.

import Greeting from './components/Greeting';

function App() {
  return (
    <div>
      <Greeting />
    </div>
  );
}

export default App;

Para correr el proyecto ejecutamos el comando:

npm run dev

3.2. JSX: JavaScript + HTML

JSX es una extensión de JavaScript que permite escribir HTML dentro de archivos JavaScript. Esto facilita la creación de componentes y mejora la legibilidad del código.

<h1>¡Hola, mundo!</h1>

JSX se compila a llamadas de funciones de React que crean elementos de React.

Por ejemplo, el código JSX anterior se compila a:

React.createElement('h1', null, '¡Hola, mundo!');

Sin embargo no es necesario preocuparse por esto ya que React se encarga de hacerlo por nosotros.

Tip

Nota: Los elementos JSX deben tener un solo contenedor raíz. Si necesitas devolver varios elementos, puedes envolverlos en un contenedor div o usar Fragment.

Ejemplo de Fragment:

function App() {
  return (
    <>
      <h1>¡Hola, mundo!</h1>
      <p>Bienvenido a mi aplicación.</p>
    </>
  );
}

En el ejemplo anterior, usamos Fragment para envolver múltiples elementos sin necesidad de un contenedor adicional.

Para poder probar este ejemplo es necesario modificar el archivo App.js. y correr el servidor de desarrollo.

npm run dev

3.3. Props y Estado

Props: Son los “argumentos” que los componentes reciben para configurar su contenido o comportamiento.

Para entender mejor cómo funcionan las props, vamos a modificar el componente Greeting para que reciba un prop llamado name.

En el directorio components, modificamos el archivo llamado Greeting.jsx con el siguiente contenido:

export default function Greeting(props) {
  return <h1>¡Hola, {props.name}!</h1>;
}

Muy bien ahora vamos a modificar el archivo App.js para que el componente Greeting reciba el prop name.

import Greeting from './components/Greeting';

function App() {
  return (
    <>
      <Greeting name="Diego" />
    </>
  );
}

export default App;

En el ejemplo anterior, pasamos el prop name con el valor “Diego” al componente Greeting. Esto permite personalizar el saludo que se muestra en la pantalla.

3.4. Hooks

Los hooks fueron introducidos en React 16.8 y son funciones que permiten a los componentes funcionales gestionar el estado y otros efectos del ciclo de vida, funcionalidades que anteriormente solo estaban disponibles en los componentes de clase.

useState: Gestiona el estado dentro de un componente funcional.

useEffect: Maneja efectos secundarios, como llamadas a APIs o actualizaciones del DOM.

Ejemplo con useState y useEffect:

import { useState, useEffect } from 'react';

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Has hecho clic ${count} veces`;
  }, [count]); // Se ejecuta cada vez que 'count' cambia

  return (
    <div>
      <p>Has hecho clic {count} veces</p>
      <button onClick={() => setCount(count + 1)}>Haz clic</button>
    </div>
  );
}

export default App;

En el código anterior, usamos useState para inicializar el estado count en 0 y setCount para actualizarlo. También usamos useEffect para actualizar el título de la página cada vez que count cambia.

Para entender mejor este concepto vamo a crear un nuevo componente llamado Clasificador de Edades.

En el directorio components, creamos un archivo llamado AgeClassifier.jsx con el siguiente contenido:

import { useState } from 'react';

export default function AgeClassifier() {
  const [age, setAge] = useState(0);
  const [classification, setClassification] = useState('');

  function classifyAge() {
    if (age < 18) {
      setClassification('Menor de edad');
    } else {
      setClassification('Mayor de edad');
    }
  }

  return (
    <div>
      <input
        type="number"
        value={age}
        onChange={(e) => setAge(parseInt(e.target.value))}
      />
      <button onClick={classifyAge}>Clasificar Edad</button>
      <p>{classification}</p>
    </div>
  );
}

En el código anterior, usamos useState para inicializar el estado age en 0 y classification en una cadena vacía. Luego, definimos una función classifyAge que clasifica la edad en “Menor de edad” o “Mayor de edad” según el valor de age. Finalmente, mostramos un campo de entrada para la edad, un botón para clasificarla y un mensaje con la clasificación.

Para probar este componente, vamos a importarlo y usarlo en el archivo App.js.

import AgeClassifier from './components/AgeClassifier';

function App() {
  return (
    <>
      <AgeClassifier />
    </>
  );
}

export default App;

Ahora, al correr el servidor de desarrollo, podrás ver el componente AgeClassifier en acción.

npm run dev

Con este ejercicio hemos aprendido a usar los hooks useState y useEffect en un componente funcional de React. Estos hooks son fundamentales para gestionar el estado y los efectos secundarios en aplicaciones React modernas.

4. Diferencias entre el Uso Antiguo y el Actual de React

Antes: Componentes de Clase

En versiones anteriores, los desarrolladores usaban componentes de clase para manejar el estado y el ciclo de vida de los componentes.

class Greeting extends React.Component {
  render() {
    return <h1>¡Hola, {this.props.name}!</h1>;
  }
}

Ahora: Componentes Funcionales con Hooks

Hoy en día, la norma es usar componentes funcionales con hooks, ya que son más concisos y fáciles de trabajar.

function Greeting({ name }) {
  return <h1>¡Hola, {name}!</h1>;
}

Ventajas del enfoque moderno con Hooks:

Menos código: Los componentes funcionales con hooks son más cortos y claros.

Manejo de estado más simple: useState y otros hooks ofrecen una forma directa y sencilla de gestionar el estado y el ciclo de vida.

Mejor rendimiento: React puede optimizar mejor los componentes funcionales.

Microproyecto con React

Para finalizar este módulo vamos a crear un microproyecto con React y Vite. En este proyecto vamos a crear una aplicación de lista de tareas (To-Do List) que permita agregar, eliminar y marcar tareas como completadas.

1. Crear un Proyecto con Vite

Para comenzar, vamos a crear un nuevo proyecto React con Vite. Abre tu terminal y ejecuta el siguiente comando:

npm create vite@latest todo-list
cd todo-list
npm install

Este comando creará un nuevo proyecto React con Vite llamado todo-list y lo instalará en tu directorio actual.

2. Crear Componentes

En el directorio src/components, crea un nuevo archivo llamado TodoList.jsx con el siguiente contenido:

import { useState } from 'react';

export default function TodoList() {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  function addTask() {
    if (newTask.trim() !== '') {
      setTasks([...tasks, { id: tasks.length + 1, text: newTask, completed: false }]);
      setNewTask('');
    }
  }

  function deleteTask(id) {
    setTasks(tasks.filter((task) => task.id !== id));
  }

  function toggleTask(id) {
    setTasks(tasks.map((task) => {
      if (task.id === id) {
        return { ...task, completed: !task.completed };
      }
      return task;
    }));
  }

  return (
    <div>
      <h1>Lista de Tareas</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="Nueva tarea"
      />
      <button onClick={addTask}>Agregar Tarea</button>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>
            <input
              type="checkbox"
              checked={task.completed}
              onChange={() => toggleTask(task.id)}
            />
            <span style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>
              {task.text}
            </span>
            <button onClick={() => deleteTask(task.id)}>Eliminar</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

En el código anterior, creamos un componente funcional TodoList que gestiona una lista de tareas. Usamos el hook useState para mantener el estado de las tareas y la nueva tarea que se está escribiendo. También definimos funciones para agregar, eliminar y marcar tareas como completadas.

3. Usar el Componente TodoList

En el archivo src/App.js, importa y usa el componente TodoList que acabamos de crear:

import TodoList from './components/TodoList';

function App() {
  return (
    <div>
      <TodoList />
    </div>
  );
}

export default App;

4. Ejecutar el Proyecto

Finalmente, ejecuta el servidor de desarrollo para ver tu aplicación de lista de tareas en acción:

npm run dev

Con estos pasos, has creado una aplicación de lista de tareas funcional con React y Vite. Puedes agregar, eliminar y marcar tareas como completadas, demostrando cómo los componentes, el estado y los hooks de React trabajan juntos para crear aplicaciones web interactivas y dinámicas.

Reto

Mejorar la aplicación de lista de tareas agregando las siguientes funcionalidades:

  • Editar Tareas: Permite editar el texto de una tarea existente.

  • Filtrar Tareas: Agrega botones para filtrar las tareas por estado (completadas, pendientes, todas).

  • Guardar Tareas: Implementa la capacidad de guardar las tareas en el almacenamiento local del navegador para que persistan entre sesiones.

  • Estilos Personalizados: Añade estilos CSS personalizados para mejorar la apariencia de la aplicación.

🔍 Solución

Para implementar estas funcionalidades, puedes seguir los siguientes pasos:

  1. Editar Tareas: Agrega un botón de edición a cada tarea que permita cambiar su texto.
function editTask(id, text) {
  const updatedText = prompt('Editar tarea:', text);
  if (updatedText !== null) {
    setTasks(tasks.map((task) => {
      if (task.id === id) {
        return { ...task, text: updatedText };
      }
      return task;
    }));
  }
}
  1. Filtrar Tareas: Agrega botones para filtrar las tareas por estado.
function filterTasks(status) {
  switch (status) {
    case 'completed':
      return tasks.filter((task) => task.completed);
    case 'pending':
      return tasks.filter((task) => !task.completed);
    default:
      return tasks;
  }
}
  1. Guardar Tareas: Utiliza el almacenamiento local del navegador para guardar las tareas.
useEffect(() => {
  const savedTasks = JSON.parse(localStorage.getItem('tasks'));
  if (savedTasks) {
    setTasks(savedTasks);
  }
}, []);

useEffect(() => {
  localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);

El archivo TodoList.jsx completo con estas mejoras se vería así:

import { useState, useEffect } from 'react';

export default function TodoList() {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  useEffect(() => {
    const savedTasks = JSON.parse(localStorage.getItem('tasks'));
    if (savedTasks) {
      setTasks(savedTasks);
    }
  }, []);

  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]);

  function addTask() {
    if (newTask.trim() !== '') {
      setTasks([...tasks, { id: tasks.length + 1, text: newTask, completed: false }]);
      setNewTask('');
    }
  }

  function deleteTask(id) {
    setTasks(tasks.filter((task) => task.id !== id));
  }

  function toggleTask(id) {
    setTasks(tasks.map((task) => {
      if (task.id === id) {
        return { ...task, completed: !task.completed };
      }
      return task;
    }));
  }

  function editTask(id, text) {
    const updatedText = prompt('Editar tarea:', text);
    if (updatedText !== null) {
      setTasks(tasks.map((task) => {
        if (task.id === id) {
          return { ...task, text: updatedText };
        }
        return task;
      }));
    }
  }

  function filterTasks(status) {
    switch (status) {
      case 'completed':
        return tasks.filter((task) => task.completed);
      case 'pending':
        return tasks.filter((task) => !task.completed);
      default:
        return tasks;
    }
  }

  return (
    <div>
      <h1>Lista de Tareas</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="Nueva tarea"
      />
      <button onClick={addTask}>Agregar Tarea</button>
      <ul>
        {filterTasks('all').map((task) => (
          <li key={task.id}>
            <input
              type="checkbox"
              checked={task.completed}
              onChange={() => toggleTask(task.id)}
            />
            <span style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>
              {task.text}
            </span>
            <button onClick={() => editTask(task.id, task.text)}>
              Editar
            </button>
            <button onClick={() => deleteTask(task.id)}>
              Eliminar
            </button>
          </li>
        ))}
      </ul>
      <div>
        <button onClick={() => setTasks(filterTasks('all'))}>
          Todas
        </button>
        <button onClick={() => setTasks(filterTasks('completed'))}>
          Completadas
        </button>
        <button onClick={() => setTasks(filterTasks('pending'))}>
          Pendientes
        </button>
      </div>
    </div>
  );
}
  1. Estilos Personalizados: Crea un archivo CSS en el directorio src y agrega estilos personalizados.
body {
  font-family: Arial, sans-serif;
}

button {
  padding: 0.5rem 1rem;
  margin: 0.5rem;
  cursor: pointer;
}

li {
  display: flex;
  align-items: center;
  margin: 0.5rem 0;
}

input[type="checkbox"] {
  margin-right: 1rem;
}

span {
  flex: 1;
}

button {
  background-color: #f44336;
  color: white;
  border: none;
  border-radius: 4px;
}

button:hover {
  background-color: #d32f2f;
}

button:active {
  background-color: #b71c1c;
}

input[type="text"] {
  padding: 0.5rem;
  margin-right: 1rem;
}

input[type="checkbox"] {
  transform: scale(1.5);
}

ul {
  list-style-type: none;
  padding: 0;
}

h1 {
  text-align: center;
}

button.add {
  background-color: #4caf50;
}

button.add:hover {
  background-color: #388e3c;
}

button.add:active {
  background-color: #2e7d32;
}

button.edit {
  background-color: #2196f3;
}

button.edit:hover {
  background-color: #1e88e5;
}

button.edit:active {
  background-color: #1976d2;
}

button.delete {
  background-color: #f44336;
}

button.delete:hover {
  background-color: #d32f2f;
}

button.delete:active {
  background-color: #b71c1c;
}

button.filter {
  background-color: #9e9e9e;
}

button.filter:hover {
  background-color: #757575;
}

button.filter:active {
  background-color: #424242;
}

button.clear {
  background-color: #f44336;
}

button.clear:hover {
  background-color: #d32f2f;
}

button.clear:active {
  background-color: #b71c1c;
}

button.save {
  background-color: #4caf50;
}

button.save:hover {
  background-color: #388e3c;
}

button.save:active {
  background-color: #2e7d32;
}

button.cancel {
  background-color: #f44336;
}

button.cancel:hover {
  background-color: #d32f2f;
}

button.cancel:active {
  background-color: #b71c1c;
}

button.toggle {
  background-color: #4caf50;
}

button.toggle:hover {
  background-color: #388e3c;
}

button.toggle:active {
  background-color: #2e7d32;
}

button.toggle-all {
  background-color: #9e9e9e;
}

button.toggle-all:hover {
  background-color: #757575;
}

button.toggle-all:active {
  background-color: #424242;
}

button.clear-completed {
  background-color: #f44336;
}

button.clear-completed:hover {
  background-color: #d32f2f;
}

button.clear-completed:active {
  background-color: #b71c1c;
}

button.clear-all {
  background-color: #f44336;
}

button.clear-all:hover {
  background-color: #d32f2f;
}

button.clear-all:active {
  background-color: #b71c1c;
}

button.save-all {
  background-color: #4caf50;
}

button.save-all:hover {
  background-color: #388e3c;
}

button.save-all:active {
  background-color: #2e7d32;
}

button.cancel-all {
  background-color: #f44336;
}

button.cancel-all:hover {
  background-color: #d32f2f;
}

button.cancel-all:active {
  background-color: #b71c1c;
}

button.filter-all {
  background-color: #9e9e9e;
}

button.filter-all:hover {
  background-color: #757575;
}

button.filter-all:active {
  background-color: #424242;
}

button.filter-completed {
  background-color: #9e9e9e;
}

button.filter-completed:hover {
  background-color: #757575;
}

button.filter-completed:active {
  background-color: #424242;
}

button.filter-pending {
  background-color: #9e9e9e;
}

button.filter-pending:hover {
  background-color: #757575;
}

button.filter-pending:active {
  background-color: #424242;
}

button.filter-clear {
  background-color: #f44336;
}

button.filter-clear:hover {
  background-color: #d32f2f;
}

button.filter-clear:active {
  background-color: #b71c1c;
}

button.filter-save {
  background-color: #4caf50;
}

button.filter-save:hover {
  background-color: #388e3c;
}

button.filter-save:active {
  background-color: #2e7d32;
}

button.filter-cancel {
  background-color: #f44336;
}

button.filter-cancel:hover {
  background-color: #d32f2f;
}

button.filter-cancel:active {
  background-color: #b71c1c;
}

button.filter-toggle {
  background-color: #4caf50;
}

button.filter-toggle:hover {
  background-color: #388e3c;
}

button.filter-toggle:active {
  background-color: #2e7d32;
}

button.filter-toggle-all {
  background-color: #9e9e9e;
}

button.filter-toggle-all:hover {
  background-color: #757575;
}

button.filter-toggle-all:active {
  background-color: #424242;
}

button.filter-clear-completed {
  background-color: #f44336;
}

button.filter-clear-completed:hover {
  background-color: #d32f2f;
}

button.filter-clear-completed:active {
  background-color: #b71c1c;
}

button.filter-clear-all {
  background-color: #f44336;
}

button.filter-clear-all:hover {
  background-color: #d32f2f;
}

button.filter-clear-all:active {
  background-color: #b71c1c;
}

button.filter-save-all {
  background-color: #4caf50;
}

button.filter-save-all:hover {
  background-color: #388e3c;
}

button.filter-save-all:active {
  background-color: #2e7d32;
}

button.filter-cancel-all {
  background-color: #f44336;
}

button.filter-cancel-all:hover {
  background-color: #d32f2f;
}

button.filter-cancel-all:active {
  background-color: #b71c1c;
}

button.filter-all-all {
  background-color: #9e9e9e;
}

button.filter-all-all:hover {
  background-color: #757575;
}

button.filter-all-all:active {
  background-color: #424242;
}

button.filter-completed-completed {
  background-color: #9e9e9e;
}

button.filter-completed-completed:hover {
  background-color: #757575;
}

button.filter-completed-completed:active {
  background-color: #424242;
}

button.filter-pending-pending {
  background-color: #9e9e9e;
}

button.filter-pending-pending:hover {
  background-color: #757575;
}

button.filter-pending-pending:active {
  background-color: #424242;
}

button.filter-clear-clear {
  background-color: #f44336;
}

Con estas mejoras, tu aplicación de lista de tareas será más interactiva, funcional y atractiva visualmente. ¡Sigue experimentando y mejorando tus habilidades con React!

Conclusión: React Antes y Ahora con Vite

React ha evolucionado significativamente desde sus inicios, pasando de componentes de clase y callbacks a componentes funcionales y hooks. La introducción de Vite ha mejorado aún más la experiencia de desarrollo al proporcionar una configuración ligera y un rendimiento excepcional.