react-router v6: obtenga el patrón de ruta para la ruta actual

5 minutos de lectura

avatar de usuario de caseyjhol
caseyjhol

¿Es posible obtener el patrón de ruta para la ruta actualmente coincidente? Ejemplo:

<Route
    path=":state/:city*"
    element={
        <Page />
    }
/>
// Page.jsx

function Page() {
    ...
    // usePathPattern doesn't actually exist
    const pathPattern = usePathPattern(); // pathPattern = ":state/:city*"
    ...
}

Sé que puedo usar useMatch para verificar si la ubicación actual coincide con un patrón de ruta específico, pero luego el componente debe saber cuál es el patrón de ruta.

Avatar de usuario de Futian Shen
Futián Shen

Hice un gancho personalizado useCurrentPath con react-router v6 para obtener la ruta actual de la ruta, y me funciona

Si el nombre de ruta actual es /members/5566 conseguiré el camino /members/:id

import { matchRoutes, useLocation } from "react-router-dom"

const routes = [{ path: "/members/:id" }]

const useCurrentPath = () => {
  const location = useLocation()
  const [{ route }] = matchRoutes(routes, location)

  return route.path
}

function MemberPage() {
  const currentPath = useCurrentPath() // `/members/5566` -> `/members/:id`
   
  return <></>
}

Referencia

https://reactrouter.com/en/v6.3.0/api#matchroutes

  • Esto funciono muy bien para mi. El gancho personalizado es una preferencia personal. Sin embargo, useLocation + matchRoutes es la respuesta.

    – Herman J. Radtke III

    1 de mayo a las 19:33

  • Esto no parece funcionar con rutas anidadas con rutas relativas. por ejemplo, cuando location.pathname es "/users/123/profile" pero routes[0].path es "profile" debido a que está anidado debajo "users/:id". ¿Tienes una solución para eso?

    – eliw00d

    28 de julio a las 20:56


avatar de usuario de m.wrona
m.wrona

No estoy seguro de si resuelve completamente su caso de uso, pero en mi caso usé una combinación de useLocation y useParams. Aquí está el código:

import React from 'react';
import { useLocation, useParams } from 'react-router-dom';
import type { Location, Params } from 'react-router-dom';

/**
 * Function converts path like /user/123 to /user/:id
 */
const getRoutePath = (location: Location, params: Params): string => {
  const { pathname } = location;

  if (!Object.keys(params).length) {
    return pathname; // we don't need to replace anything
  }

  let path = pathname;
  Object.entries(params).forEach(([paramName, paramValue]) => {
    if (paramValue) {
      path = path.replace(paramValue, `:${paramName}`);
    }
  });
  return path;
};

export const Foo = (): JSX.Element => {
  const location = useLocation();
  const params = useParams();
  const path = getRoutePath(location, params);

  (...)
};

  • Esta solución se rompería cuando las partes de la ruta tengan el mismo valor, por ejemplo /user/1/badge/1/comment/1. Si bien ese es un escenario poco común, esta solución aún no es universalmente confiable.

    – Ben Barkay

    30 abr a las 12:47


  • mejor usar reactrouter.com/docs/en/v6/api#matchroutes en cambio.

    – Herman J. Radtke III

    1 de mayo a las 19:32

  • El enlace de @HermanJ.RadtkeIII parece estar roto. tal vez te refieres este

    – Reimirno

    7 de julio a las 3:51

Desde dentro del contexto de un Route puedes conseguir el path a través de varios métodos establecidos en los documentos.

Mi problema era ligeramente diferente en el sentido de que necesitaba el path fuera del contexto de la Route y llegó a la siguiente solución que también debería funcionar para usted:

import {
    matchPath,
    useLocation
} from "react-router-dom";

const routes = [ ':state/:city', ...otherRoutes ];

function usePathPattern() {

    const { pathname } = useLocation();

    return matchPath( pathname, routes )?.path;
}

  • Su enlace de documentación apunta a los documentos v5, no a los documentos v6, y por lo tanto no es útil.

    – Chris Kitching

    16 de enero a las 4:25

  • Documentación v6: reactrouter.com/docs/en/v6/utils/match-path

    – aderchox

    15 de junio a las 7:08

Avatar de usuario de RichN
RicoN

Esto parece funcionar con lo que realmente exportan a partir de 6.2.1, sin embargo, utiliza un componente que exportan como UNSAFE_

import { UNSAFE_RouteContext } from 'react-router-dom';

const reconstructPath = (matches) =>
  matches
    .map(({ route: { path } }) =>
      path.endsWith('/*') ? path.slice(0, -1) : path ? path + "https://stackoverflow.com/" : ''
    )
    .join('');

const findLastNode = (node) =>
  node.outlet ? findLastNode(node.outlet.props.value) : node;

const usePathPattern = () =>
  reconstructPath(
    findLastNode(React.useContext(UNSAFE_RouteContext)).matches
  );

esto me funciona facilmente

en primer lugar, importe la ubicación de uso desde react-router-dom

import { useLocation } from "react-router-dom"

después

const location = useLocation();
console.log(location.pathname);

  • Esto da la ruta literal, no el patrón de ruta.

    – Antonio O.

    10 ene a las 19:39

Avatar de usuario de Tobi
Tobi

Escribí un enlace personalizado para ese propósito, ya que no parece ser compatible con oob en este momento:

Nota: aún no se ha probado a fondo. Así que úsalo con precaución.

import { useLocation, useParams } from 'react-router';

export function useRoutePathPattern() {
  const routeParams = useParams();
  const location = useLocation();

  let routePathPattern = location.pathname;

  Object.keys(routeParams)
    .filter((paramKey) => paramKey !== '*')
    .forEach((paramKey) => {
      const paramValue = routeParams[paramKey];
      const regexMiddle = new RegExp(`\/${paramValue}\/`, 'g');
      const regexEnd = new RegExp(`\/${paramValue}$`, 'g');

      routePathPattern = routePathPattern.replaceAll(
        regexMiddle,
        `/:${paramKey}/`,
      );
      routePathPattern = routePathPattern.replaceAll(regexEnd, `/:${paramKey}`);
    });

  return routePathPattern;
}

  • Esto da la ruta literal, no el patrón de ruta.

    – Antonio O.

    10 ene a las 19:39

Avatar de usuario de Labithiotis
labitiotis

Para encontrar la ruta coincidente en react-router v6, debe usar el matchPath función. Deberá almacenar todas sus rutas en una variable como una matriz. Luego puede recorrer todas las rutas y usar esa función para encontrar la ruta coincidente. Con cuidado, ya que coincidirá con el primer valor real, idealmente debe recorrerlos en el mismo orden en que los ha representado.

Esta no es una solución perfecta, ya que es posible que no maneje rutas anidadas a menos que las almacene en la misma variable.

Aquí hay un ejemplo:

rutas.ts

const routes = [
  { name: 'Home', path: '/home', element: Home },
  { name: 'Profile', path: '/profile', element: Profile },
  { name: '404', path: '*', element: NotFound },
];

Aplicación.tsx

<App>
  <BrowserRouter>
    <Header />
    <Routes>
      {routes.map((route, key) => (
        <Route key={key} path={route.path} element={<route.element />} />
      ))}
    </Routes>
  </BrowserRouter>
</App

usarMatchedRoute.tsx

import { matchPath } from 'react-router';

export function useMatchedRoute() {
  const { pathname } = useLocation();
  for (const route of routes) {
    if (matchPath({ path: route.path }, pathname)) {
      return route;
    }
  }
}

Encabezado.tsx

export function Header() {
  const route = useMatchedRoute();

  return <div>{route.name}</div>
}

¿Ha sido útil esta solución?