DEV Community

Gino Luraschi
Gino Luraschi

Posted on

React v19, unboxing! 📦

Introduccion

React v19 acaba de salir. Hoy en día, es una de las bibliotecas más populares del mundo, destacada por su simplicidad y su eficiente manejo del DOM. Este año se lanzó una nueva versión, y aquí te traigo todas las novedades, con ejemplos prácticos, como siempre, pero en español 👀.

Actions

Las Actions son transiciones asincrónicas que realizas, como llamadas al backend para actualizar estados. Esta novedad facilita el manejo de formularios en React, incorporando nuevos hooks, como por ejemplo el useTransaction():

  • Cómo manejábamos los formularios antes:
// Before Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    }
    redirect("/path");
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • Con el uso de useTransaction()
// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      // Esta transicion es una llamada a una API asincrona
      // Aca isPending = true
      const error = await updateName(name);
      if (error) {
        setError(error);
        return; // Aca isPending = false
      }
      redirect("/path"); // Aca isPending = false
    });
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Nuevos hooks

useActionState

Una manera de simplificar los llamados a las Actions es utilizando el nuevo hook useActionState

// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    // los valores del campo son los nombres de los inputs
    async (previousState, formData) => {
      // isPending es true
      const error = await updateName(formData.get("name"));
      if (error) {
        return error; // esto settea el error, isPending es false
      }
      redirect("/path");
      return null; // isPending es false
    },
    null
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

New hook: useOptimistic

El nuevo hook useOptimistic se utiliza para cambiar el resultado de un valor mientras se realiza una petición, sin necesidad de esperar el resultado y siendo optimista respecto al resultado esperado de la petición.
En este ejemplo se muestra cómo podemos establecer el nombre antes de realizar la petición:

function ChangeName({ currentName, onUpdateName }) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async (formData) => {
    const newName = formData.get("name");
    setOptimisticName(newName); // Aca se muestra el nombre actual antes de confirmarse el cambio
    const updatedName = await updateName(newName);
    onUpdateName(updatedName); // Aca confirmamos el cambio del nombre
  };

  return (
    <form action={submitAction}>
      <p>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

React DOM

Actions

Como vimos en useActionState, ahora el campo del formulario puede recibir nuevos atributos, como action y formAction, para trabajar con <form>, <input> y <button>.

Algo interesante al trabajar con acciones es que, cuando el formulario se envía correctamente, React resetea automáticamente los formularios. Si deseas resetear el formulario manualmente, puedes llamar a requestFormReset de la API del React DOM.

New hook: useFormStatus

Otra herramienta interesante que se suma a esta nueva versión de React es el hook useFormStatus, que permite utilizar el campo isPending de los hooks mencionados anteriormente. Esto es útil cuando tenemos, por ejemplo, un componente de carga. Podemos utilizar el estado del formulario padre para mostrar y ocultar este componente de carga. La forma de utilizar este hook puede ser la siguiente:

// Este componente es llamado desde donde se implemente nuestro formulario
import { useFormStatus } from "react-dom";

function LoadingComponent() {
  const { pending } = useFormStatus();
  return pending ? <>Loading...</> : <></>;
}

useFormStatus lee el estado del formulario padre como si fuera un Context Provider.

Nuevas APIs

use

En la nueva versión de React se agregó use para leer recursos en tiempo de renderizado.

Por ejemplo, podemos leer una promesa con use y React suspenderá el renderizado hasta que la promesa se resuelva:

const DataComponent: React.FC = () => {
  const user = use(fetchUser()); // fetch user es asincrono

  return (
    <div>
      <h1>{user.name}</h1>
    </div>
  );
};

export default DataComponent;

SSG y Server components

Para Static Site Generation (SSG), React agregó 2 nuevas APIs:

  • prerender
  • prerenderToNodeStream

En cuanto a los server components:
Los Server Components de React permiten renderizar componentes anticipadamente en un entorno separado del cliente o del servidor SSR, llamado "server" en los React Server Components. Pueden ejecutarse una vez durante la construcción en un servidor CI o por solicitud en un servidor web. React 19 incluye todas las características de los Server Components, lo que permite a las bibliotecas compatibles usarlas como dependencia con una condición de exportación react-server para frameworks con arquitectura Full-stack React.

Los actions en SSR:
Las Server Actions permiten que los Client Components llamen funciones asincrónicas ejecutadas en el servidor. Al definir una Server Action con la directiva use server, el framework crea automáticamente una referencia a la función del servidor y la pasa al Client Component. Cuando esta función se llama desde el cliente, React envía una solicitud al servidor para ejecutarla y devuelve el resultado.

Algunas mejoras en React 19

ref as a prop
Ahora podes acceder a ref como una prop para functional components:

import React, { forwardRef, useRef } from "react";

// Aca recibimos ref como prop
const MyInput = forwardRef((props, ref) => {
  return <input type="text" placeholder={props.placeholder} ref={ref} />;
});

const MyForm = () => {
  const inputRef = useRef(null);

  const focusInput = () => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  return (
    <form>
      <MyInput placeholder="Escribe algo..." ref={inputRef} />
      <button type="button" onClick={focusInput}>
        Focalizar Input
      </button>
    </form>
  );
};

export default MyForm;

En el futuro se eliminara forwardRef.

Diffs for hydration errors
También se eliminarán algunos errores en la consola en el momento de la hidratación (Más info aquí)

<Context> como Provider

Ahora se utliza <Context> en lugar de <Context.Provider>:

const CartContext = createContext('');

function App({children}) {
  return (
    <CartContext value={{ cart, addItem, substractItem, removeItem, cleanCart }}>
      {children}
    </CartContext>
  );  
}

Cleanup functions para refs
Ahora React 19 soporta el retorno de una funcion para limpiar los ref callbacks:

<input
  ref={(ref) => {
    // ref es creado
    // mi codigo
    // Este codigo se ejecuta cuando el componente es quitado del DOM
    return () => {
      // codigo de limpieza
    };
  }}
/>

useDeferredValue ahora acepta un valor inicial
Se agrego una nueva opcion para inicializar el valor de un deferredValue

function Search({deferredValue}) {
  // El valor inicial es ''
  // Se planea un inicio tardio para el valor de query
  const value = useDeferredValue(deferredValue, '');

  return (
    <Results query={value} />
  );
}

Soporte para metadata
Ahora se agrego el soporte para metadata en React que antes solo se podia hacer en el tag <head> o a traves de librerias:

function BlogPost({post}) {
  // En el componente articulo estamos definiendo meta y link, que antes no se podia
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>
      <meta name="author" content="Josh" />
      <link rel="author" href="https://twitter.com/joshcstory/" />
      <meta name="keywords" content={post.keywords} />
      <p>
        Eee equals em-see-squared...
      </p>
    </article>
  );
}

En cuanto a las hojas de estilo:
Ya sean externas o en línea, requieren una ubicación cuidadosa en el DOM debido a las reglas de precedencia. Esto complica su uso dentro de componentes, lo que lleva a cargar estilos lejos de los componentes o a usar bibliotecas que gestionen esta complejidad. React 19 simplifica este proceso con soporte integrado para hojas de estilo, permitiendo especificar su precedencia. React gestiona el orden de inserción en el DOM y asegura que, si son externas, se carguen antes de mostrar el contenido que depende de ellas.

function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo" precedence="default" />
      <link rel="stylesheet" href="bar" precedence="high" />
      <article class="foo-class bar-class">
        {...}
      </article>
    </Suspense>
  )
}

function ComponentTwo() {
  // Esta hoja de estilo sera insertada entre foo y bar
  // En caso de existir una hoja de estilo igual en otro componente, solo se incluira una
  // Ademas se inluyo la posibilidad de renderizar asincrona
  return (
    <div>
      <p>{...}</p>
      <link rel="stylesheet" href="baz" precedence="default" />  
      <link rel="stylesheet" href="bar" async={true} precedence="high" />
    </div>
  )
}

Durante el renderizado del lado del servidor (SSR), React incluirá la hoja de estilo en el

para garantizar que el navegador no pinte el contenido hasta que esta se haya cargado. Si la hoja de estilo se detecta tarde, tras iniciar el streaming, React la insertará en el del cliente antes de mostrar el contenido de un límite de Suspense que dependa de ella. Durante el renderizado del lado del cliente (CSR), React esperará a que las hojas de estilo recién renderizadas se carguen antes de comprometer el renderizado, y si el componente se renderiza en varios lugares de la aplicación, React incluirá la hoja de estilo solo una vez en el documento.

Como actualizar?

Para actualizar hay una guia de actualizacion de React donde se explica detallado como hacerlo.

Fuente

Nada de esto es obra mía, solo es una pequeña traducción y resumen de lo que hay en este post en inglés: React v19

Conclusion

Se agradece la lectura y espero que les ayude. Algunas aclaraciones importantes:

  • Utilicé los ejemplos de la página porque me parecieron muy claros; otros los intenté adaptar a un ejemplo más práctico, y otros los generé con mi amigo GitHub Copilot.
  • Varios textos los generé con IA, mi nuevo gran amigo. Cualquier comentario o sugerencia siempre es bienvenido.

Top comments (0)