Hoja de Trucos de React

Aprende Desarrollo Frontend con React y Laboratorios Prácticos

Aprende desarrollo frontend con React a través de laboratorios prácticos y escenarios del mundo real. LabEx ofrece cursos completos de React que cubren la creación esencial de componentes, gestión de estado, hooks, manejo de eventos y optimización del rendimiento. Domina la construcción de interfaces de usuario eficientes y mantenibles para aplicaciones web modernas.

Creación de Componentes y JSX

Componentes Funcionales: function / =>

Crea componentes usando sintaxis de función.

import React from 'react'

// Declaración de función
function Welcome(props) {
  return <h1>Hola, {props.name}!</h1>
}

// Función de flecha
const Welcome = (props) => {
  return <h1>Hola, {props.name}!</h1>
}

// Retorno implícito para componentes simples
const Greeting = ({ name }) => <h1>Hola, {name}!</h1>

Componentes de Clase: class extends React.Component

Crea componentes usando sintaxis de clase ES6.

import React, { Component } from 'react'

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

// Con constructor
class Counter extends Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
  }
  render() {
    return <div>Contador: {this.state.count}</div>
  }
}

Elementos JSX: <element>

Escribe sintaxis similar a HTML dentro de JavaScript.

// Elemento JSX
const element = <h1>Hola, mundo!</h1>

// JSX con expresiones
const name = 'John'
const greeting = <h1>Hola, {name}!</h1>

// JSX de varias líneas
const element = (
  <div>
    <h1>¡Bienvenido!</h1>
    <p>Es bueno verte por aquí.</p>
  </div>
)

Exportación de Componentes: export default / export

Exporta componentes para su uso en otros archivos.

// Exportación por defecto
export default function App() {
  return <div>Mi App</div>
}

// Exportación nombrada
export const Button = () => <button>Haz clic</button>

Importación de Componentes: import

Importa componentes desde otros archivos.

// Importar componente por defecto
import App from './App'

// Importar componente nombrado
import { Button } from './Button'

// Importar múltiples componentes
import React, { useState, useEffect } from 'react'

// Importar con alias
import { Button as MyButton } from './Button'

Fragmento: <React.Fragment> / <>

Agrupa elementos sin añadir nodos DOM extra.

// Usando React.Fragment
return (
  <React.Fragment>
    <h1>Título</h1>
    <p>Descripción</p>
  </React.Fragment>
)

// Usando sintaxis corta
return (
  <>
    <h1>Título</h1>
    <p>Descripción</p>
  </>
)

Props y Estructura de Componentes

Props: props.name

Pasa datos de componentes padre a hijo.

// Recibiendo props
function Welcome(props) {
  return <h1>Hola, {props.name}!</h1>
}

// Desestructuración de props
function Welcome({ name, age }) {
  return (
    <h1>
      Hola, {name}! Tienes {age} años.
    </h1>
  )
}

// Props por defecto
function Welcome({ name = 'Invitado' }) {
  return <h1>Hola, {name}!</h1>
}
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Cómo pasas datos de un componente padre a un componente hijo en React?
Usando variables de estado
Usando props
Usando refs
Usando la API de contexto

PropTypes: Component.propTypes

Valida las props pasadas a los componentes (requiere el paquete prop-types).

import PropTypes from 'prop-types'

function Welcome({ name, age }) {
  return (
    <h1>
      Hola, {name}! Edad: {age}
    </h1>
  )
}

Welcome.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
}

Welcome.defaultProps = {
  age: 18,
}

Children: props.children

Accede al contenido pasado entre las etiquetas de apertura/cierre del componente.

// Componente que usa children
function Card({ children }) {
  return <div className="card">{children}</div>
}

// Uso
;<Card>
  <h2>Título</h2>
  <p>Contenido aquí</p>
</Card>

Gestión de Estado y Hooks

Hook useState: useState()

Añade estado a componentes funcionales.

import React, { useState } from 'react'

function Counter() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <p>Contador: {count}</p>
      <button onClick={() => setCount(count + 1)}>Incrementar</button>
    </div>
  )
}

// Múltiples variables de estado
function Form() {
  const [name, setName] = useState('')
  const [email, setEmail] = useState('')
}
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué devuelve useState(0)?
Un array con el valor del estado y una función para actualizarlo
Solo el valor del estado
Una función para actualizar el estado
Nada, solo establece el estado

Hook useEffect: useEffect()

Realiza efectos secundarios en componentes funcionales.

import React, { useState, useEffect } from 'react'

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

  // El efecto se ejecuta después de cada renderizado
  useEffect(() => {
    document.title = `Contador: ${count}`
  })

  // Efecto con limpieza
  useEffect(() => {
    const timer = setInterval(() => setCount((c) => c + 1), 1000)
    return () => clearInterval(timer)
  }, [])
}
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué significa el array de dependencias vacío [] en useEffect(() => {...}, [])?
El efecto se ejecuta en cada renderizado
El efecto nunca se ejecuta
El efecto se ejecuta dos veces
El efecto se ejecuta solo una vez después del renderizado inicial

Estado de Clase: this.state / setState()

Gestiona el estado en componentes de clase.

class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
  }
  increment = () => {
    this.setState({ count: this.state.count + 1 })
  }
  render() {
    return (
      <div>
        <p>Contador: {this.state.count}</p>
        <button onClick={this.increment}>Incrementar</button>
      </div>
    )
  }
}

Hooks Personalizados: use...

Crea lógica de estado reutilizable.

// Hook personalizado
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue)
  const increment = () => setCount(count + 1)
  const decrement = () => setCount(count - 1)
  const reset = () => setCount(initialValue)
  return { count, increment, decrement, reset }
}

// Uso
function Counter() {
  const { count, increment, decrement, reset } = useCounter(0)
  return (
    <div>
      <p>Contador: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={reset}>Reiniciar</button>
    </div>
  )
}

Manejo de Eventos

Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Cuál es el propósito de PropTypes en React?
Validar los tipos de props pasadas a los componentes
Mejorar el rendimiento de los componentes
Estilizar componentes automáticamente
Hacer que los componentes sean más rápidos

Eventos de Clic: onClick

Maneja clics de botones e interacciones de elementos.

function Button() {
  const handleClick = () => {
    alert('¡Botón presionado!')
  }
  return <button onClick={handleClick}>Haz clic</button>
}

// Manejador de eventos en línea
function Button() {
  return <button onClick={() => alert('¡Presionado!')}>Haz clic</button>
}

// Pasando parámetros
function Button() {
  const handleClick = (message) => {
    alert(message)
  }
  return <button onClick={() => handleClick('¡Hola!')}>Haz clic</button>
}

Eventos de Formulario: onChange / onSubmit

Maneja entradas de formulario y envíos.

function Form() {
  const [value, setValue] = useState('')
  const handleChange = (e) => {
    setValue(e.target.value)
  }
  const handleSubmit = (e) => {
    e.preventDefault()
    console.log('Enviado:', value)
  }
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={value}
        onChange={handleChange}
        placeholder="Introduce texto"
      />
      <button type="submit">Enviar</button>
    </form>
  )
}

Objeto de Evento: event.target / event.preventDefault()

Accede a las propiedades del evento y controla el comportamiento predeterminado.

function handleInput(event) {
  console.log('Valor de entrada:', event.target.value)
  console.log('Nombre de entrada:', event.target.name)
}

function handleFormSubmit(event) {
  event.preventDefault() // Previene el envío del formulario
  console.log('Formulario enviado')
}

// Delegación de eventos
function List() {
  const handleClick = (event) => {
    if (event.target.tagName === 'BUTTON') {
      console.log('Botón presionado:', event.target.textContent)
    }
  }
  return (
    <div onClick={handleClick}>
      <button>Botón 1</button>
      <button>Botón 2</button>
    </div>
  )
}

Eventos de Teclado: onKeyDown / onKeyUp

Responde a las interacciones del teclado.

function KeyboardHandler() {
  const handleKeyDown = (event) => {
    if (event.key === 'Enter') {
      console.log('Tecla Enter presionada')
    }
    if (event.ctrlKey && event.key === 's') {
      event.preventDefault()
      console.log('Ctrl+S presionado')
    }
  }
  return <input onKeyDown={handleKeyDown} placeholder="Escribe aquí..." />
}

Renderizado Condicional

Operadores Condicionales: && / ?:

Muestra/oculta elementos basados en condiciones.

function Greeting({ user }) {
  return (
    <div>
      {user && <h1>Bienvenido, {user.name}!</h1>}
      {!user && <h1>Por favor, inicia sesión</h1>}
    </div>
  )
}

// Operador ternario
function Status({ isOnline }) {
  return <div>El usuario está {isOnline ? 'en línea' : 'desconectado'}</div>
}

Lógica If/Else: Declaraciones if

Usa lógica tradicional de JavaScript para condiciones complejas.

function UserProfile({ user, isAdmin }) {
  if (!user) {
    return <div>Cargando...</div>
  }
  if (isAdmin) {
    return <AdminPanel user={user} />
  }
  return <UserPanel user={user} />
}

// Patrón de retorno temprano
function Component({ data }) {
  if (!data) return null
  if (data.error) return <ErrorMessage />
  return <DataDisplay data={data} />
}

Sentencias Switch: switch

Maneja múltiples condiciones de manera eficiente.

function StatusIcon({ status }) {
  switch (status) {
    case 'loading':
      return <Spinner />
    case 'success':
      return <CheckIcon />
    case 'error':
      return <ErrorIcon />
    default:
      return null
  }
}

Estilos Dinámicos: CSS Condicional

Aplica estilos basados en el estado o las props del componente.

function Button({ variant, disabled }) {
  const className = `btn ${variant} ${disabled ? 'disabled' : ''}`
  return (
    <button
      className={className}
      style={{
        backgroundColor: variant === 'primary' ? 'blue' : 'gray',
        opacity: disabled ? 0.5 : 1,
      }}
      disabled={disabled}
    >
      Haz clic
    </button>
  )
}

Renderizado de Listas y Claves (Keys)

Función Map: array.map()

Renderiza listas de componentes a partir de datos de arrays.

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  )
}

// Con índice (evitar cuando sea posible)
function ItemList({ items }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  )
}

Claves (Keys): Prop key

Proporciona identificadores únicos para los elementos de la lista para optimizar el renderizado.

// Bueno: usando ID único
function UserList({ users }) {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          <UserCard user={user} />
        </li>
      ))}
    </ul>
  )
}

// Creando claves compuestas
function CommentList({ comments }) {
  return (
    <div>
      {comments.map((comment) => (
        <Comment key={`${comment.postId}-${comment.id}`} comment={comment} />
      ))}
    </div>
  )
}

Filter y Map: Métodos de Array

Procesa arrays antes de renderizarlos.

function TaskList({ tasks, showCompleted }) {
  const filteredTasks = showCompleted
    ? tasks
    : tasks.filter((task) => !task.completed)
  return (
    <ul>
      {filteredTasks.map((task) => (
        <li key={task.id} className={task.completed ? 'completed' : ''}>
          {task.title}
        </li>
      ))}
    </ul>
  )
}

Estados Vacíos: Manejo de arrays vacíos

Muestra contenido apropiado cuando las listas están vacías.

function ProductList({ products }) {
  if (products.length === 0) {
    return <div>No se encontraron productos.</div>
  }
  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}

Optimización del Rendimiento

React.memo: React.memo()

Previene renderizados innecesarios de componentes funcionales.

const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
  return <div>{/* Lógica de renderizado compleja */}</div>
})

// Con comparación personalizada
const MyComponent = React.memo(
  function MyComponent({ user }) {
    return <div>{user.name}</div>
  },
  (prevProps, nextProps) => {
    return prevProps.user.id === nextProps.user.id
  },
)

Hook useMemo: useMemo()

Memoriza cálculos costosos.

function ExpensiveList({ items, searchTerm }) {
  const filteredItems = useMemo(() => {
    return items.filter((item) =>
      item.name.toLowerCase().includes(searchTerm.toLowerCase()),
    )
  }, [items, searchTerm])
  return (
    <ul>
      {filteredItems.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  )
}

Hook useCallback: useCallback()

Memoriza referencias de funciones para prevenir renderizados innecesarios.

function Parent({ items }) {
  const [count, setCount] = useState(0)
  const handleItemClick = useCallback((itemId) => {
    console.log('Elemento presionado:', itemId)
  }, []) // Array de dependencias vacío
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Contador: {count}</button>
      <ItemList items={items} onItemClick={handleItemClick} />
    </div>
  )
}

Carga Diferida (Lazy Loading): React.lazy() / Suspense

Carga componentes solo cuando son necesarios para reducir el tamaño del paquete.

const LazyComponent = React.lazy(() => import('./LazyComponent'))

function App() {
  return (
    <div>
      <Suspense fallback={<div>Cargando...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  )
}

Comunicación entre Componentes

Props Hacia Abajo: Padre a Hijo

Pasa datos de componentes padre a componentes hijo.

function Parent() {
  const [user, setUser] = useState({ name: 'John', age: 30 })
  return (
    <div>
      <ChildComponent user={user} />
      <AnotherChild userName={user.name} />
    </div>
  )
}

function ChildComponent({ user }) {
  return <div>Hola, {user.name}!</div>
}

Callbacks Hacia Arriba: Hijo a Padre

Envía datos de componentes hijo de vuelta a componentes padre.

function Parent() {
  const [message, setMessage] = useState('')
  const handleChildMessage = (msg) => {
    setMessage(msg)
  }
  return (
    <div>
      <p>Mensaje: {message}</p>
      <Child onMessage={handleChildMessage} />
    </div>
  )
}

function Child({ onMessage }) {
  return (
    <button onClick={() => onMessage('¡Hola desde el hijo!')}>
      Enviar Mensaje
    </button>
  )
}

Context API: createContext / useContext

Comparte estado a través de múltiples componentes sin “prop drilling”.

const UserContext = React.createContext()

function App() {
  const [user, setUser] = useState({ name: 'John' })
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Header />
      <Main />
    </UserContext.Provider>
  )
}

function Header() {
  const { user } = useContext(UserContext)
  return <h1>Bienvenido, {user.name}!</h1>
}

Refs: useRef / forwardRef

Accede a elementos DOM o almacena valores mutables.

function TextInput() {
  const inputRef = useRef(null)
  const focusInput = () => {
    inputRef.current.focus()
  }
  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Enfocar Entrada</button>
    </div>
  )
}

// Reenvío de refs
const FancyInput = forwardRef((props, ref) => (
  <input className="fancy" ref={ref} {...props} />
))

Herramientas de Desarrollo y Depuración

React DevTools: Extensión del Navegador

Depura componentes de React e inspecciona el árbol de componentes.

// Instalar la extensión del navegador React DevTools
// Pestaña Components: Inspeccionar jerarquía de componentes
// Pestaña Profiler: Medir el rendimiento

// Depuración en consola
function MyComponent(props) {
  console.log('Props de MyComponent:', props)
  console.log('MyComponent renderizado')
  return <div>{props.children}</div>
}

Límites de Error (Error Boundaries): componentDidCatch

Captura errores de JavaScript en el árbol de componentes y muestra una UI de respaldo.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = { hasError: false }
  }
  static getDerivedStateFromError(error) {
    return { hasError: true }
  }
  componentDidCatch(error, errorInfo) {
    console.log('Error capturado:', error, errorInfo)
  }
  render() {
    if (this.state.hasError) {
      return <h1>Algo salió mal.</h1>
    }
    return this.props.children
  }
}

Strict Mode: React.StrictMode

Habilita verificaciones y advertencias adicionales para el desarrollo.

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root'),
)

Perfilado (Profiling): Medición de rendimiento

Mide el rendimiento de los componentes e identifica cuellos de botella.

// Usando el Profiler de React DevTools
// Envuelve componentes para perfilar
import { Profiler } from 'react'

function onRenderCallback(id, phase, actualDuration) {
  console.log('Componente', id, 'tomó', actualDuration, 'ms')
}

;<Profiler id="App" onRender={onRenderCallback}>
  <App />
</Profiler>

Instalación y Configuración de React

Create React App: npx create-react-app

Crea rápidamente un nuevo proyecto React.

# Crear nueva app de React
npx create-react-app mi-app
cd mi-app

# Iniciar servidor de desarrollo
npm start

# Construir para producción
npm run build

# Ejecutar pruebas
npm test

Vite: npm create vite@latest

Herramienta de compilación rápida y servidor de desarrollo para proyectos React.

# Crear nueva app de Vite React
npm create vite@latest mi-app-react -- --template react
cd mi-app-react
npm install

# Iniciar servidor de desarrollo
npm run dev

# Construir para producción
npm run build

Configuración Manual / Importación

Añade React a un proyecto existente o usa CDN.

# Instalar React y ReactDOM
npm install react react-dom

# Para desarrollo
npm install --save-dev @vitejs/plugin-react
// Importación básica de React
import React from 'react'
import ReactDOM from 'react-dom/client'

// Renderizar en el DOM
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)

Patrones y Características Avanzadas

Componentes de Orden Superior (HOC)

Reutiliza la lógica de componentes envolviendo componentes.

function withLoading(WrappedComponent) {
  return function WithLoadingComponent(props) {
    if (props.isLoading) {
      return <div>Cargando...</div>
    }
    return <WrappedComponent {...props} />
  }
}

// Uso
const UserListWithLoading = withLoading(UserList)
;<UserListWithLoading users={users} isLoading={loading} />

Patrón Render Props

Comparte código entre componentes usando una prop cuyo valor es una función.

function DataFetcher({ render, url }) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => {
        setData(data)
        setLoading(false)
      })
  }, [url])
  return render({ data, loading })
}

// Uso
;<DataFetcher
  url="/api/users"
  render={({ data, loading }) =>
    loading ? <Spinner /> : <UserList users={data} />
  }
/>

Componentes Compuestos

Crea componentes que trabajan juntos como una unidad cohesiva.

function Tabs({ children, activeTab }) {
  return (
    <div className="tabs">
      {React.Children.map(children, (child, index) =>
        React.cloneElement(child, { isActive: index === activeTab }),
      )}
    </div>
  )
}

function Tab({ children, isActive }) {
  return <div className={`tab ${isActive ? 'active' : ''}`}>{children}</div>
}

// Uso
;<Tabs activeTab={0}>
  <Tab>Contenido de Pestaña 1</Tab>
  <Tab>Contenido de Pestaña 2</Tab>
</Tabs>

Portal: ReactDOM.createPortal()

Renderiza hijos en un nodo DOM fuera de la jerarquía del componente padre.

import ReactDOM from 'react-dom'

function Modal({ children, isOpen }) {
  if (!isOpen) return null
  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal">{children}</div>
    </div>,
    document.getElementById('modal-root'),
  )
}

Composición sobre Herencia

Usa patrones de composición en lugar de extender clases.

// Bueno: Composición
function Button({ variant, children, ...props }) {
  return (
    <button className={`btn btn-${variant}`} {...props}>
      {children}
    </button>
  )
}

function IconButton({ icon, children, ...props }) {
  return (
    <Button {...props}>
      <Icon name={icon} />
      {children}
    </Button>
  )
}

Patrones de Componentes: APIs Flexibles

Diseña APIs de componentes que sean flexibles y fáciles de usar.

// Componente Card Flexible
function Card({ header, children, footer, variant = 'default' }) {
  return (
    <div className={`card card-${variant}`}>
      {header && <div className="card-header">{header}</div>}
      <div className="card-body">{children}</div>
      {footer && <div className="card-footer">{footer}</div>}
    </div>
  )
}

// Uso
;<Card header={<h3>Título</h3>} footer={<Button>Acción</Button>}>
  Contenido de la tarjeta aquí
</Card>

Enlaces Relevantes