Skip to content

React Router v6

En esta sección aprenderemos a trabajar con rutas en nuestra aplicación con React.

⭐ Videos Premium ⭐

Esta sección es parte del curso en Udemy. Si quieres acceder a ella, puedes comprar el curso en Udemy: React + Firebase by bluuweb.

O bien también puedes visualizar los videos siendo miembro en Youtube. Accede a miembros del canal aquí y accede a los videos de esta sección.

Router v6

  • react router v6
  • React Router es una biblioteca de enrutamiento del lado del servidor y del cliente con todas las funciones para React.
  • React Router se ejecuta en cualquier lugar donde se ejecute React; en la web, en el servidor con node.js y en React Native.
npm create vite@latest router-tutorial
cd router-tutorial
npm i
npm i react-router-dom@6
npm run dev

Enrutador

Si bien su aplicación solo usará un solo enrutador, hay varios enrutadores disponibles según el entorno en el que se esté ejecutando su aplicación.

picking a router

En v6.4, se introdujeron nuevos enrutadores que admiten las nuevas API de datos:

  • createBrowserRouter
  • createMemoryRouter
  • createHashRouter

Los siguientes enrutadores no admiten las API de datos:

  • <BrowserRouter>
  • <MemoryRouter>
  • <HashRouter>
  • <NativeRouter>
  • <StaticRouter>

BrowserRouter

Lo primero es lo primero, queremos conectar su aplicación a la URL del navegador: importarla BrowserRouter y renderizarla alrededor de toda su aplicación.

BrowserRouter Es el componente que se encarga de conectar su aplicación a la URL del navegador.

main.jsx

jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

import { BrowserRouter } from "react-router-dom";

ReactDOM.createRoot(document.getElementById("root")).render(
    <React.StrictMode>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </React.StrictMode>
);

Navbar.jsx

jsx
import { Link } from "react-router-dom";

const Navbar = () => {
    return (
        <nav className="navbar navbar-dark bg-dark">
            <div className="container">
                <Link className="btn btn-outline-primary" to="/">
                    Inicio
                </Link>
                <Link className="btn btn-outline-primary" to="/blog">
                    Blog
                </Link>
                <Link className="btn btn-outline-primary" to="/contacto">
                    Contacto
                </Link>
            </div>
        </nav>
    );
};

export default Navbar;

App.jsx

jsx
import Navbar from "./components/Navbar";

const App = () => {
    return (
        <div>
            <Navbar />
            <div className="container">
                <h1>App</h1>
            </div>
        </div>
    );
};

export default App;

Agregar Rutas

  • src/routes/Blog.jsx
  • src/routes/Contacto.jsx
  • src/routes/Inicio.jsx

index.jsx

jsx
import Navbar from "./components/Navbar";
import Inicio from "./pages/Inicio";
import Blog from "./pages/Blog";
import Contacto from "./pages/Contacto";

import { Routes, Route } from "react-router-dom";

const App = () => {
    return (
        <>
            <Navbar />
            <Routes>
                <Route path="/" element={<Inicio />} />
                <Route path="/blog" element={<Blog />} />
                <Route path="/contacto" element={<Contacto />} />
            </Routes>
        </>
    );
};

export default App;

Rutas anidadas (Layout)

Las rutas anidadas se pueden usar para crear un diseño de página (Layout) que se repite en varias páginas.

Paso 1:

jsx
<Routes>
    <Route path="/" element={<LayoutPage />}>
        <Route index element={<Inicio />} />
        <Route path="/blog" element={<Blog />} />
        <Route path="/contacto" element={<Contacto />} />
    </Route>
</Routes>

Paso 2: Layout.jsx

jsx
import { Outlet } from "react-router-dom";

const LayoutPage = () => {
    return (
        <div className="container">
            <Outlet />
        </div>
    );
};

export default LayoutPage;

Ruta indice

jsx
<Routes>
    <Route path="/" element={<LayoutPage />}>
        <Route index element={<Inicio />} />
        <Route path="/blog" element={<Blog />} />
        <Route path="/contacto" element={<Contacto />} />
    </Route>
</Routes>
  • Observe que tiene el index prop en lugar de path.
  • Eso es porque la ruta del índice comparte la ruta del padre.
  • Las rutas de índice coinciden cuando una ruta principal coincide, pero ninguna de las otras secundarias coincide.
  • Las rutas de índice son la ruta secundaria predeterminada para una ruta principal.
  • Las rutas de índice se representan cuando el usuario aún no ha hecho clic en uno de los elementos de una lista de navegación.
jsx
import { NavLink } from "react-router-dom";

const Navbar = () => {
    return (
        <nav className="navbar navbar-dark bg-dark">
            <div className="container">
                <NavLink className="btn btn-outline-primary" to="/">
                    Inicio
                </NavLink>
                <NavLink className="btn btn-outline-primary" to="/blog">
                    Blog
                </NavLink>
                <NavLink className="btn btn-outline-primary" to="/contacto">
                    Contacto
                </NavLink>
            </div>
        </nav>
    );
};

export default Navbar;

404

jsx
import { Link } from "react-router-dom";

const NoEncontrada = () => {
    return (
        <div>
            <h1>404</h1>
            <Link to="/" className="btn btn-outline-dark">
                Inicio
            </Link>
        </div>
    );
};

export default NoEncontrada;
jsx
<Routes>
    <Route path="/" element={<LayoutPage />}>
        <Route index element={<Inicio />} />
        <Route path="/blog" element={<Blog />} />
        <Route path="/contacto" element={<Contacto />} />
        <Route path="*" element={<NotFound />} />
    </Route>
</Routes>

Parámetros

hooks/useFetch.js

js
import { useCallback, useEffect, useState } from "react";

export const useFetch = (url) => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    const getData = useCallback(async () => {
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error("Error fetching data");
            }
            const data = await response.json();
            setData(data);
        } catch (error) {
            setError(error.message);
        } finally {
            setLoading(false);
        }
    }, [url]);

    useEffect(() => {
        getData();
    }, [getData]);

    return { data, loading, error };
};

Blog.jsx

jsx
import { Link } from "react-router-dom";
import { useFetch } from "../hooks/useFetch";

const Blog = () => {
    const { data, error, loading } = useFetch(
        "https://jsonplaceholder.typicode.com/posts"
    );

    if (loading) {
        return <h1>Loading...</h1>;
    }

    if (error) {
        return <h1>{error}</h1>;
    }

    return (
        <div>
            <h1>Blog</h1>
            {data.map((item) => (
                <h4 key={item.id}>
                    <Link to={`/blog/${item.id}`}>
                        {item.id} - {item.title}
                    </Link>
                </h4>
            ))}
        </div>
    );
};

export default Blog;
jsx
<Route path="/" element={<LayoutPage />}>
    <Route index element={<Inicio />} />
    <Route path="/blog" element={<Blog />} />
    <Route path="/blog/:id" element={<Post />} />
    <Route path="/contacto" element={<Contacto />} />
    <Route path="*" element={<NotFound />} />
</Route>

Post.jsx

jsx
import { useParams } from "react-router";
import { useFetch } from "../hooks/useFetch";

const Post = () => {
    let params = useParams();

    const { data, error, loading } = useFetch(
        "https://jsonplaceholder.typicode.com/posts/" + params.id
    );

    if (loading) {
        return <h1>Loading...</h1>;
    }

    if (error) {
        return <h1>{error}</h1>;
    }

    return (
        <div>
            <h1>
                {data.id} - {data.title}
            </h1>
            <p>{data.body}</p>
        </div>
    );
};

export default Post;

Parámetros de búsqueda

  • React Router hace que sea fácil de leer y manipular los parámetros de búsqueda con useSearchParams
  • El useSearchParams se utiliza para leer y modificar la cadena de consulta en la URL de la ubicación actual.
  • Funciona de manera muy similar, React.useState() pero almacena y establece el estado en los parámetros de búsqueda de URL en lugar de en la memoria.

Blog.jsx

jsx
let [searchParams, setSearchParams] = useSearchParams();

useEffect(() => {
    // http://localhost:3000/blog?nombre=juan
    console.log(searchParams.get("nombre"));
    // setSearchParams({ nombre: "juan" });
}, [searchParams]);

Blog.jsx

jsx
import { Link, useSearchParams } from "react-router-dom";
import { useFetch } from "../hooks/useFetch";

const Blog = () => {
    let [searchParams, setSearchParams] = useSearchParams();

    const { data, error, loading } = useFetch(
        "https://jsonplaceholder.typicode.com/posts"
    );

    if (loading) {
        return <h1>Loading...</h1>;
    }

    if (error !== "") {
        return <h1>{error}</h1>;
    }

    const handleChange = (e) => {
        let filter = e.target.value;
        if (filter) {
            setSearchParams({ filter });
        } else {
            setSearchParams({});
        }
    };

    return (
        <div>
            <h1>Blog</h1>
            <input
                className="form-control mb-2"
                type="text"
                value={searchParams.get("filter") || ""}
                onChange={handleChange}
            />
            {data
                .filter((item) => {
                    let filter = searchParams.get("filter");
                    if (!filter) return true;
                    let name = item.title.toLowerCase();
                    return name.startsWith(filter.toLowerCase());
                })
                .map((item) => (
                    <h4 key={item.id}>
                        <Link to={`/blog/${item.id}`}>
                            {item.id} - {item.title}
                        </Link>
                    </h4>
                ))}
        </div>
    );
};

export default Blog;

Netlify + queryParams

  • fuente
  • Crear archivo _redirects en public con:
/* /index.html 200
npm run build

Subir a netlify 🎉 https://www.netlify.com/