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.
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.
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
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>
);
Link
Navbar.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
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
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:
<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
import { Outlet } from "react-router-dom";
const LayoutPage = () => {
return (
<div className="container">
<Outlet />
</div>
);
};
export default LayoutPage;
Ruta indice
<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 depath
. - 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.
Enlaces activos (NavLink)
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
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;
<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
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
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;
<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
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
let [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
// http://localhost:3000/blog?nombre=juan
console.log(searchParams.get("nombre"));
// setSearchParams({ nombre: "juan" });
}, [searchParams]);
Blog.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
enpublic
con:
/* /index.html 200
npm run build
Subir a netlify 🎉 https://www.netlify.com/