Fundamentos

Características principales de React

  • Componentes: React está basado en la componetización de la UI. La interfaz se divide en componentes independientes, que contienen su propio estado. Cuando el estado de un componente cambia, React vuelve a renderizar la interfaz.

  • Virtual DOM: React usa un DOM virtual para renderizar los componentes. El DOM virtual es una representación en memoria del DOM real. Cuando el estado de un componente cambia, React vuelve a renderizar la interfaz. En lugar de modificar el DOM real, React modifica el DOM virtual y, a continuación, compara el DOM virtual con el DOM real. De esta forma, React sabe qué cambios se deben aplicar al DOM real.

  • Declarativo: React es declarativo, lo que significa que no se especifica cómo se debe realizar una tarea, con otras librerías, como jQuery, o Vanilla Javascript, el tipo de programación que utilizamos es imperativo. Se crean scripts que tienen que informar en el DOM qué debe de realizar o cómo debe de actuar. Esta forma de declarar hace que tengamos que escribir mucho código y a la larga un engorro a la hora de mantenerlo.

    La primera de las ventajas de React es que contamos con un estado de la aplicación y sus componentes responden ante la variación de ese estado. Cuando cambiamos las propiedades de los componentes se produce un cambio en su funcionalidad por detras.

  • Unidireccional:

    Cuando decimos que ReactJS tiene un flujo de datos unidireccional, nos referimos a que los datos en una aplicación React fluyen en una sola dirección: de los componentes padres hacia los componentes hijos.

    Detalle del Flujo Unidireccional:

    1. Props: Los datos o propiedades (props) se pasan de un componente padre a un componente hijo. Los componentes hijos reciben estas props y las utilizan para renderizar la UI o para realizar otras acciones.

    2. Estado: El estado de la aplicación normalmente se maneja en componentes padres o en un estado centralizado. Los cambios en el estado del padre afectan a los hijos, porque el padre pasa esos datos a los hijos como props.

    3. Inmutabilidad: Los hijos no pueden modificar directamente los datos que reciben a través de las props. Si un hijo necesita cambiar algo, debe comunicar ese cambio al padre mediante un evento o callback, para que el padre actualice el estado y, a su vez, pase los datos actualizados de nuevo hacia abajo.

  • Universal: React se puede ejecutar tanto en el cliente como en el servidor. Además, puedes usar React Native para crear aplicaciones nativas para Android e iOS.

Componentes y elementos

Un componente es una pieza de código que renderiza una parte de la interfaz. Los componentes pueden ser parametrizados, reutilizados y pueden contener su propio estado.

En React los componentes se crean usando funciones o clases que reciben props y devuelve un elemento. Un elemento es un objeto que representa un nodo del DOM o una instancia de un componente de React.

JSX

JSX es una extensión de la sintaxis de JavaScript que te permite escribir código que se parece a HTML dentro de archivos JavaScript. Se utiliza principalmente en React para describir cómo debería verse la interfaz de usuario. Aunque parece HTML, JSX es convertido a JavaScript puro antes de ser ejecutado en el navegador.

Características de JSX:

  • Sintaxis Similar a HTML: JSX permite escribir elementos de UI de una manera que se parece mucho a HTML, lo que facilita la lectura y escritura de componentes de interfaz de usuario.

  • Transpilación: Antes de ser ejecutado por el navegador, JSX se transpila (generalmente usando Babel) a llamadas de funciones de JavaScript estándar. Por ejemplo, <div>Hello, World!</div> se transpila a React.createElement('div', null, 'Hello, World!').

  • Expresiones JavaScript: Dentro de JSX, puedes usar cualquier expresión de JavaScript dentro de llaves {}.

Uso en Otros Frameworks:

Si bien JSX es más comúnmente usado con React, también puede ser usado en otros frameworks o bibliotecas que se integren con el ecosistema de Babel. Algunos ejemplos incluyen:

  1. Preact: Una biblioteca similar a React pero más ligera, que también soporta JSX.

  2. Inferno: Otra biblioteca de UI rápida que puede usar JSX.

  3. Vue.js: Aunque Vue usa templates en su mayoría, también soporta JSX si se configura el entorno adecuadamente.

Props

Las props son las propiedades de un componente. Son datos que se pasan de un componente padre a otro hijo de manera unidireccional. Por ejemplo, si tienes un componente Button que muestra un botón, puedes pasarle una prop text para que el botón muestre ese texto:

function Button(props) {
  return <button>{props.text}</button>
}

// Para usarlo
<Button text="Haz clic aquí" />

Prop children

La prop children es una prop especial que se pasa a los componentes. Es un objeto que contiene los elementos que envuelve un componente.

Por ejemplo, si tenemos un componente Card que muestra una tarjeta con un título y un contenido, podemos usar la prop children para mostrar el contenido:

function Card(props) {
  return (
    <div className="card">
      <h2>{props.title}</h2>
      <div>{props.children}</div>
    </div>
  )
}

Y luego podemos usarlo de la siguiente forma:

<Card title="Título de la tarjeta">
  <p>Contenido de la tarjeta</p>
</Card>  

Fragments

Los Fragments son una forma de agrupar elementos sin añadir un elemento extra al DOM, ya que React no permite devolver varios elementos en un componente, solo un elemento raíz.

Para crear un Fragment en React usamos el componente Fragment:

import { Fragment } from 'react'

function App() {
  return (
    <Fragment>
      <h1>Titulo</h1>
      <p>Párrafo</p>
    </Fragment>
  )
}

También podemos usar la sintaxis de abreviatura:

function App() {
  return (
    <>
      <h1>Titulo</h1>
      <p>Párrafo</p>
    </>
  )
}

Nota: <Fragment>: Permite agregar ciertas propiedades como key, lo cual es útil en casos donde se necesitan fragmentos con elementos iterados. Sin embargo la versión <> No permite agregar key.

Estilos en línea a un componente

Para aplicar estilos CSS en línea a un componente en React usamos la prop style. La diferencia de cómo lo haríamos con HTML, es que en React los estilos se pasan como un objeto y no como una cadena de texto (esto puede verse más claro con los dobles corchetes, los primeros para indicar que es una expresión JavaScript, y los segundos para crear el objeto):

function Button({ text }) {
  return (
    <button style={{ color: 'red', borderRadius: '2px' }}>
      {text}
    </button>
  )
}

Además, los nombres de las propiedades CSS están en camelCase.

CSS Modules

CSS Modules es una técnica para escribir CSS en aplicaciones React (y en otros proyectos de frontend) que permite que cada archivo CSS se comporte de manera local y encapsulada por defecto. Esto ayuda a evitar conflictos de nombres de clases y estilos globales no deseados.

Características principales de CSS Modules:

  1. Encapsulación de Estilos:

    • Los estilos definidos en un archivo CSS Module son locales al componente donde se importan y no se aplican globalmente. Esto evita que los estilos se filtren a otros componentes involuntariamente.

  2. Generación de Nombres Únicos:

    • Cuando se compila, cada clase en un CSS Module recibe un nombre único generado automáticamente. Por ejemplo, una clase .button en un CSS Module podría convertirse en algo como .button_xyz123 en el CSS final, asegurando que no haya conflictos con otras clases .button en la aplicación.

  3. Uso en React:

    • Los CSS Modules se importan en un componente de React como un objeto, donde cada clave corresponde al nombre de la clase definida en el archivo CSS, y el valor es el nombre de clase generado.

Ejemplo de Uso de CSS Modules en React:

  1. Definir el CSS Module:

    • Crea un archivo CSS con la extensión .module.css.

    /* Button.module.css */
    .button {
      background-color: blue;
      color: white;
      padding: 10px;
      border: none;
      border-radius: 5px;
    }
  2. Importar el CSS Module en un Componente:

    • Importa el CSS Module en tu componente de React.

    import React from 'react';
    import styles from './Button.module.css';
    
    function Button() {
      return <button className={styles.button}>Click Me</button>;
    }
    
    export default Button;
    • Aquí, styles.button se refiere a la clase .button definida en Button.module.css, pero será única y encapsulada.

  3. Uso de Múltiples Clases:

    • Puedes combinar múltiples clases usando templates literals o el paquete classnames.

    <div className={`${styles.container} ${styles.active}`} />

Ventajas de CSS Modules:

  • Evita conflictos globales de nombres de clases al mantener los estilos locales por defecto.

  • Mejor mantenimiento: facilita la gestión y organización de estilos, especialmente en aplicaciones grandes.

  • Soporte para variabilidad de nombres de clases: permite el uso de dinámicos y compuestos nombres de clases.

Renderizado de listas

El renderizado de listas es la forma de iterar un array de elementos y renderizar elementos de React para cada uno de ellos.

Para hacer renderizado de listas en React usamos el método map de los arrays:

function List({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  )
}

El elemento li tiene una prop key que es un identificador único para cada elemento. Esto es necesario para que React pueda identificar cada elemento de la lista y actualizarlo de forma eficiente. Más adelante hay una explicación más detallada sobre esto.

Esto es equivalente a:

function List({ items }) {
  return (
    <ul>
      {items.map(item => {
        return <li key={item.id}>{item.name}</li>;
      })}
    </ul>
  );
}

¿Cuándo usar return explícito?

Usar return explícito dentro del map es recomendable en las siguientes circunstancias:

  1. Lógica más Compleja: Si necesitas realizar más cálculos, condicionales, o manipular datos antes de renderizar el elemento.

    function List({ items }) {
      return (
        <ul>
          {items.map(item => {
            // Realizar alguna lógica o transformación
            const transformedName = item.name.toUpperCase();
    
            return (
              <li key={item.id}>
                {transformedName}
                {/* Puedes agregar más elementos o lógica aquí */}
              </li>
            );
          })}
        </ul>
      );
    }
  2. Uso de Condicionales: Si quieres condicionar qué elementos se renderizan.

    function List({ items }) {
      return (
        <ul>
          {items.map(item => {
            if (!item.name) {
              return null; // Omitir si el nombre no existe
            }
    
            return <li key={item.id}>{item.name}</li>;
          })}
        </ul>
      );
    }
  3. Mejor Legibilidad: Cuando el contenido dentro del map es extenso, un return explícito puede mejorar la legibilidad.

    function List({ items }) {
      return (
        <ul>
          {items.map(item => {
            // Manipulación o lógica adicional
            const additionalInfo = item.info ? ` - ${item.info}` : '';
    
            return (
              <li key={item.id}>
                {item.name}{additionalInfo}
              </li>
            );
          })}
        </ul>
      );
    }

Eventos

function Button({ id, text, onClick }) {
  const handleClick = (event) => { // handleClick recibe el evento original
    onClick(id)
  }

  return (
    <button onClick={handleClick}>
      {text}
    </button>
  )
}

Para pasar un parámetro a una función que maneja un evento en React podemos usar una función anónima:

function Button({ id, text, onClick }) {
  return (
    <button onClick={() => onClick(id)}>
      {text}
    </button>
  )
}

Cuando el usuario hace clic en el botón, se ejecuta la función onClick pasándole como parámetro el valor de la prop id al componente padre.

El estado

El estado es la forma en que React guarda información de nuestro componente para escuchar cuando tenga cambios y disparar un nuevo render, y de este modo controlar los cambios en la interfaz.

El "Estado" es una de las principales herramientas que React nos proporciona para crear UI´s dinámicas y actualizadas en tiempo real.

Para crear un estado en React usamos el hook useState:

import { useState } from 'react'

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

  return (
    <div>
      <p>Contador: {count}</p>
      <button onClick={() => setCount(count + 1)}>Aumentar</button>
    </div>
  )
}

Al usar el hook useState este devolverá un array de dos posiciones:

  1. El valor del estado.

  2. La función para cambiar el estado.

Suele usarse desestructuración para facilitar la lectura y ahorrarnos algunas líneas de código. Por otro lado, al pasarle un dato como parámetro al useState le estamos indicando su estado inicial.

Con un componente de clase, la creación del estado sería así:

import { Component } from 'react'

class Counter extends Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
  }

  render() {
    return (
      <div>
        <p>Contador: {this.state.count}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Aumentar
        </button>
      </div>
    )
  }
}

En React, "stateful" y "stateless" son términos que se refieren a dos tipos diferentes de componentes en función de cómo manejan el estado.

  1. Stateful Components (Componentes con estado): Estos son componentes que tienen un estado interno. Pueden almacenar y modificar datos a lo largo del tiempo, lo que les permite responder a eventos y actualizar su representación en consecuencia.

  2. Stateless Components (Componentes sin estado): Estos son componentes que no tienen un estado interno propio. No pueden almacenar datos ni responder a eventos internamente. Son funciones puras que aceptan props como entrada y devuelven elementos de React. Los componentes sin estado suelen utilizarse para representar la UI basada únicamente en las props que reciben.

Actualizar el estado de un objeto en React, debe hacerse de manera que el estado se mantenga inmutable. Ejemplo.

  • El operador de propagación te permite copiar las propiedades del objeto original y luego sobrescribir las propiedades específicas que deseas actualizar.

import React, { useState } from 'react';

function App() {
  const [persona, setPersona] = useState({
    nombre: 'Juan',
    edad: 30,
    direccion: '123 Calle Falsa'
  });

  const actualizarNombre = () => {
    setPersona(prevPersona => ({
      ...prevPersona,
      nombre: 'Carlos'
    }));
  };

  return (
    <div>
      <p>Nombre: {persona.nombre}</p>
      <p>Edad: {persona.edad}</p>
      <p>Dirección: {persona.direccion}</p>
      <button onClick={actualizarNombre}>Actualizar Nombre</button>
    </div>
  );
}

export default App;
  • Explicación: ...prevPersona copia todas las propiedades actuales de persona, y luego nombre: 'Carlos' sobrescribe la propiedad nombre.

  • Si estás trabajando con estados muy complejos y profundos, puedes considerar usar una biblioteca como immer, que facilita la gestión de la inmutabilidad.

    import produce from 'immer';
    
    const actualizarEdad = () => {
      setPersona(produce(draft => {
        draft.edad += 1;
      }));
    };

Hooks

Desde que en React 16.8.0 se incluyeron los hooks, los componentes de funciones pueden hacer casi todo lo que los componentes de clase.

Aunque no hay una respuesta clara a esta pregunta, normalmente los componentes funcionales son más sencillos de leer y escribir y pueden tener un mejor rendimiento en general.

Además, los hooks solo se pueden usar en los componentes funcionales. Esto es importante, ya que con la creación de custom hooks podemos reutilizar la lógica y podría simplificar nuestros componentes.

Por otro lado, los componentes de clase nos permiten usar el ciclo de vida de los componentes, algo que no podemos hacer con los componentes funcionales donde solo podemos usar useEffect.

useEffect

Los efectos son hooks que se ejecutan o actualizan únicamente si se cumplen ciertas condiciones en cada nuevo render. La condición esta dentro del array del segundo parámetro de la funcion misma useEffect.

useEffect(()=>{
    // body
}, [condition])

Si el array esta vacio se ejecuta una sola vez al ser montado el componente.

Podemos ejecutar código cuando el componente se desmonta usando el hook useEffect y dentro devolver una función con el código que queremos ejecutar. En este caso, la función que se pasa como primer parámetro del useEffect se ejecutará cuando el componente se monte, y la función que es retornada se ejecutará cuando se desmonte.

import { useEffect } from 'react'

function Component() {
  useEffect(() => {
    console.log('El componente se ha montado')

    return () => {
      console.log('El componente se ha desmontado')
    }
  }, [])

  return <h1>Ejemplo</h1>
}

Esto es muy útil para limpiar recursos que se hayan creado en el componente, como por ejemplo, eventos del navegador o para cancelar peticiones a APIs.

useEffect(() => {
  // Creamos el controlador para abortar la petición
  const controller = new AbortController()
  // Recuperamos la señal del controlador
  const { signal } = controller
  // Hacemos la petición a la API y le pasamos como options la señal
  fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
    .then(res => res.json())
    .then(json => setMessage(json.title))
    .catch(error => {
      // Si hemos cancelado la petición, la promesa se rechaza
      // con un error de tipo AbortError
      if (error.name !== 'AbortError') {
        console.error(error.message)
      }
    })

  // Si se desmonta el componente, abortamos la petición
  return () => controller.abort()
}, [])

useId

useId es un hook para generar identificadores únicos que se pueden pasar a los atributos de las etiquetas HTML y es especialmente útil para accesibilidad.

import { useId } from 'react'

function PasswordField() {
  const passwordHintId = useId()

  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        El password debe ser de 18 letras y contener caracteres especiales
      </p>
    </>
  )
}

export default function App() {
  return (
    <>
      <h2>Choose password</h2>
      <PasswordField />
      <h2>Confirm password</h2>
      <PasswordField />
    </>
  )
}

Como ves en App estamos usando el componente dos veces. Si pusieramos una id a mano, por ejemplo password, entonces la ID no sería única y quedaría duplicada. Por eso es importante que generes la ID automáticamente con useId.

La etiqueta aria-describedby te permite especificar que dos etiquetas están relacionadas entre sí, puede generar una identificación única con useId donde incluso si PasswordField aparece varias veces en la pantalla, las identificaciones generadas no chocarán.

useRef

En el siguiente ejemplo vamos a guardar la referencia en el DOM a un elemento <input> y vamos a cambiar el foco a ese elemento cuando hacemos clic en el botón.

import { useRef } from 'react'

function TextInputWithFocusButton() {
  const inputEl = useRef(null)

  const onButtonClick = () => {
    // `current` apunta al elemento inputEl montado
    inputEl.current.focus()
  }

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  )
}

Creamos una referencia inputEl con useRef y la pasamos al elemento <input> como prop ref. Cuando el componente se monta, la referencia inputEl apunta al elemento <input> del DOM.

Para acceder al elemento del DOM, usamos la propiedad current de la referencia.

Tambien se puede usar para Guardar referencias a funciones o callbacks: Especialmente útil en situaciones donde quieres asegurarte de que una referencia a una función no cambie entre renders, o cuando manejas eventos asincrónicos.

Ejemplo: Temporizador con useRef para mantener la referencia del callback

Imagina que tienes un componente que ejecuta un temporizador, y quieres que el temporizador siga ejecutando una función específica que podría cambiar durante la vida del componente. Si simplemente usas la función directamente en el setInterval, cada vez que la función cambie, el temporizador usará la versión vieja de la función. Aquí es donde useRef puede ayudar.

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

function Timer() {
  const [count, setCount] = useState(0);
  const [delay, setDelay] = useState(1000);
  const savedCallback = useRef();

  // La función que queremos que se ejecute en cada intervalo
  const tick = () => {
    setCount(prevCount => prevCount + 1);
  };

  // Guardamos la función de tick en una referencia
  useEffect(() => {
    savedCallback.current = tick;
  }, [tick]);

  // Configuramos el intervalo que llama a la función guardada en savedCallback
  useEffect(() => {
    function tickCallback() {
      savedCallback.current();
    }

    const id = setInterval(tickCallback, delay);
    return () => clearInterval(id);
  }, [delay]);

  return (
    <div>
      <h1>Contador: {count}</h1>
      <button onClick={() => setDelay(500)}>Aumentar velocidad</button>
      <button onClick={() => setDelay(2000)}>Disminuir velocidad</button>
    </div>
  );
}

export default Timer;

Explicación del Código

  1. useRef para guardar la función:

    • const savedCallback = useRef(); crea una referencia mutable que puede mantener cualquier valor. En este caso, queremos que mantenga una referencia a la función tick.

  2. Guardar la función en la referencia:

    • useEffect(() => { savedCallback.current = tick; }, [tick]); actualiza la referencia savedCallback.current cada vez que la función tick cambia. Esto asegura que savedCallback.current siempre tenga la versión más reciente de la función tick, sin importar cuántas veces el componente se renderice.

  3. Usar la referencia en un setInterval:

    • Otro useEffect se encarga de crear el intervalo que ejecuta la función guardada en savedCallback.current cada vez que pasa el delay especificado. Aquí es clave que el setInterval utilice savedCallback.current() en lugar de tick() directamente. Esto permite que siempre se ejecute la versión más reciente de tick sin necesidad de recrear el intervalo cuando la función cambia.

  4. Control del intervalo:

    • Los botones permiten cambiar el delay del intervalo para aumentar o disminuir la velocidad a la que el contador se incrementa.

Ventajas de este enfoque:

  • Referencias constantes: useRef mantiene la referencia a la función constante entre renders, lo que evita problemas con funciones obsoletas o callbacks inconsistentes en componentes complejos.

  • Eficiencia: No se crean nuevos intervalos cada vez que el componente se renderiza. Solo se utiliza la referencia actualizada de la función, manteniendo el intervalo actual intacto.

Suscribirse a un evento

Dentro de useEffect nos podemos suscribir a eventos del navegador, como el evento resize para saber cuando el usuario cambia el tamaño de la ventana. Es importante que nos desuscribamos cuando el componente se desmonte para evitar fugas de memoria. Para ello, tenemos que devolver una función dentro del useEffect que se ejecutará cuando el componente se desmonte.

import { useEffect } from 'react'

function Window() {
  useEffect(() => {
    const handleResize = () => {
      console.log('La ventana se ha redimensionado')
    }

    window.addEventListener('resize', handleResize)

    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  return (
    <p>Abre la consola y redimensiona la ventana</p>
  )
}

Aunque normalmente los componentes de React solo cuentan con un useEffect lo cierto es que podemos tener tantos useEffect como queramos en un componente. Cada uno de ellos se ejecutará cuando se renderice el componente o cuando cambien las dependencias del efecto.

Cancelar una petición a una API en useEffect

Cuando hacemos una petición a una API, podemos cancelarla para evitar que se ejecute cuando el componente se desmonte usando AbortController como hacemos en este ejemplo:

useEffect(() => {
  // Creamos el controlador para abortar la petición
  const controller = new AbortController()
  // Recuperamos la señal del controlador
  const { signal } = controller
  // Hacemos la petición a la API y le pasamos como options la señal
  fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
    .then(res => res.json())
    .then(json => setMessage(json.title))
    .catch(error => {
      // Si hemos cancelado la petición, la promesa se rechaza
      // con un error de tipo AbortError
      if (error.name !== 'AbortError') {
        console.error(error.message)
      }
    })

  // Si se desmonta el componente, abortamos la petición
  return () => controller.abort()
}, [])

Custom Hooks

Nos permiten abstraer parte de la lógica de nuestros componentes para su posterior reutilización.

Un hook personalizado es una función que empieza con la palabra use y que puede utilizar otros hooks. Son ideales para reutilizar lógica en diferentes componentes. Por ejemplo, podemos crear un hook personalizado para extraer la gestión del estado de un contador:

// ./hooks/useCounter.js

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

  const increment = () => setCount(count + 1)
  const decrement = () => setCount(count - 1)

  return { count, increment, decrement }
}

Para usarlo en un componente:

import { useCounter } from './hooks/useCounter.js'

function Counter() {
  const { count, increment, decrement } = useCounter()

  return (
    <>
      <button onClick={decrement}>-</button>
      <span>{count}</span>
      <button onClick={increment}>+</button>
    </>
  )
}

React Context

El contexto es una forma de pasar datos a través de la jerarquía de componentes sin tener que pasar props manualmente en cada nivel.

Para crear un contexto en React usamos el hook createContext:

import { createContext } from 'react'

const ThemeContext = createContext()

Para usar el contexto, debemos envolver el árbol de componentes con el componente Provider:

<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>

Para consumir el contexto, debemos usar el hook useContext:

import { useContext } from 'react'

function Button() {
  const theme = useContext(ThemeContext)
  return <button className={theme}>Haz clic aquí</button>
}

El react context ayuda asolucionar el Prop Drilling que es cuando las props de un componente padre son enviadas a sus componentes hijos, siendo estos demaciados que se torna complejo su manipulacion.

Tambien se puede crear un provider personalizado.

// App.ts
const App = () => {

  return (
    <TodoProvider>
      <Todo /> 
    </TodoProvider>
  )
}

// TodoContext.ts
const TodoContext = createContext();

const TodoProvider = ({ children }) => {   
    const [theme, setTheme] = useState<string>('');
    return (
        <TodoContext.Provider value={{ theme }}>
            {children}
        </TodoContext.Provider>
    )
}

export { TodoContext, TodoProvider }

// Todo.ts
import { TodoContext } from 'react'

function Todo() {
  const {theme} = useContext(TodoContext)
  return <button className={theme}>Haz clic aquí</button>
}

Portales

Los React Portals permiten que algunos componentes salgan del flujo o estructura por defecto de nuestra aplicación. Con ellos podemos crear portales para teletransportar componentes entre diferentes nodos de HTML. Los componentes vivirán en lugares diferentes de la página, lo que evitará muchos problemas de CSS (como z-index y overflow), pero React seguirá manteniendo control sobre ellos para comunicarles props y estados.

// App.jsx

const App = () = (
	  ...
	<Portal user="Platzinauta" />
)

const Portal = ({ user }) => {
  ReactDOM.createPortal(
    <div>
      <p>Hi {user}</p>
    </div>,
    document.getElmentById("modal")
  )
}

Mas información en https://platzi.com/blog/react-portals/

Side Effect

En JavaScript y React, un "side effect" (efecto secundario) es cualquier operación o comportamiento que ocurre fuera del alcance o flujo principal de una función o componente, afectando el estado externo o provocando cambios observables que no están directamente relacionados con la función en sí.

En JavaScript:

En JavaScript, un side effect ocurre cuando una función modifica algo fuera de su alcance local. Esto podría ser:

  • Modificar una variable global: Si una función cambia el valor de una variable que está fuera de su propio contexto.

  • Modificar una variable externa o de una función: Si una función modifica el estado de un objeto o una variable que fue pasada como argumento.

  • Realizar operaciones de entrada/salida: Llamadas a APIs, manipulación del DOM, operaciones de archivo, etc.

  • Lanzar errores o excepciones: A veces se considera un efecto secundario si la función tiene efectos colaterales al lanzar errores.

Por ejemplo:

javascriptCopy codelet count = 0;

function increment() {
    count++;  // Esto es un side effect porque modifica una variable global.
}

increment();
console.log(count); // El valor de count ahora es 1.

En React:

En React, un side effect se refiere a cualquier operación que se realiza dentro de un componente que afecta algo fuera de la función de renderizado del componente. React tiene un ciclo de vida de componentes específico, y los side effects generalmente se manejan usando hooks como useEffect.

Ejemplos de side effects en React:

  • Fetch de datos desde una API: Si una función de React realiza una llamada a una API para obtener datos y luego actualiza el estado con esos datos.

  • Manipulación directa del DOM: Aunque React se encarga de actualizar el DOM, en algunos casos se puede necesitar manipular el DOM directamente, lo cual es considerado un side effect.

  • Suscripciones: Establecer suscripciones o listeners que necesiten ser limpiados cuando el componente se desmonte.

Ejemplo usando useEffect:

javascriptCopy codeimport React, { useState, useEffect } from 'react';

function MyComponent() {
    const [data, setData] = useState(null);

    useEffect(() => {
        // Esto es un side effect porque estamos haciendo una llamada a una API.
        fetch('https://api.example.com/data')
            .then(response => response.json())
            .then(data => setData(data));
    }, []); // El array vacío significa que este effect se ejecuta solo una vez, después de que el componente se monta.

    return (
        <div>
            <p>Data: {data}</p>
        </div>
    );
}

Aquí, la llamada a fetch es un side effect porque ocurre fuera del ciclo de renderizado principal y afecta al estado del componente.

Last updated