Я хочу дополнить маршрут реагирования-маршрутизатора-dom настраиваемыми свойствами. Каждый маршрут должен включать атрибут «имя», который будет служить описанием, отображаемым вверху страницы.
В идеале я бы хотел, чтобы компонент Route явно запрашивал и принимал эти пользовательские свойства. Это позволило бы мне получить к ним доступ в промежуточном программном обеспечении, отвечающем за аутентификацию пользователя и сборку макета страницы.
Приведенный ниже пример не работает должным образом, и в настоящее время я не уверен в причине. Ошибка, с которой я столкнулся: «Неперехваченная ошибка: [T] не является компонентом. Все дочерние компоненты должны быть a или <React.Fragment>».
import { RouteProps, Route as R } from "react-router-dom"
type Props = RouteProps & {
name?: string
}
export const T: React.FC<Props> = ({...props}) => {
return <R {...props} />
}
import { BrowserRouter, Routes } from "react-router-dom";
import { T } from "@components/Route"
const RoutesApp = () => {
return (
<BrowserRouter>
<Routes>
<T path = "/" element = {<Middleware />} >
<T path = "test" name = "This is a page" element = {<>test</>} />
</T>
</Routes>
</BrowserRouter>
);
};
export default RoutesApp;
import { Outlet } from "react-router-dom";
export const Middleware = () => {
if (...){
return <>
<main>
<h1>{name}</h1>
<Outlet />
</main>
</>
}
}
AFAIK, вы не можете расширить компонент Route
и при этом иметь возможность отображать его внутри компонентов Routes
или Route
, поскольку он больше не будет экземпляром Route
и не пройдет проверку инварианта.
Компонент Route
уже может обрабатывать «лишние» данные, передаваемые в маршруте, в виде дескриптора маршрута prop. Это работает только в новых API данных (см. Выбор маршрутизатора ). Используйте маршрут handle
, чтобы передать свойство name
, и используйте крючок useMatches в компоненте Middleware
, чтобы вычислить совпадение и извлечь name
.
Пример:
const Middleware = () => {
const matches = useMatches();
const name = matches.filter((match) => match.handle?.name).pop()?.handle.name;
return (
<>
<main>
<h1>{name}</h1>
<Outlet />
</main>
</>
);
};
const router = createBrowserRouter([
{
path: "/",
element: <Middleware />,
children: [
...,
{
path: "/test",
element: <>test</>,
handle: {
name: "This is a page"
}
},
...
]
}
]);
Если вы не хотите обновлять/переходить на API-интерфейсы DATA, вы можете использовать контекст Middleware
компонента Outlet
и компонент-оболочку для установки имени маршрута. Используйте локальное состояние React для хранения значения name
и предоставления обратного вызова установщика в контексте Outlet
. Компонент-обертка принимает свойство name
и обновляет состояние Middleware
текущим именем. Каждый маршрутизируемый компонент заворачивается в обертку для задания имени.
Пример:
const Middleware = () => {
const [name, setName] = useState();
return (
<>
<main>
<h1>{name}</h1>
<Outlet context = {{ setName }} />
</main>
</>
);
};
const Wrapper = ({ children, name }) => {
const { setName } = useOutletContext();
useEffect(() => {
setName(name);
}, [name]);
return children;
};
const router = createBrowserRouter([
{
path: "/",
element: <Middleware />,
children: [
...,
{
path: "test",
element: (
<Wrapper name = "This is a page">
test
</Wrapper>
)
},
...,
{
path: "test",
element: (
<Wrapper> // <-- "unset" name
test 2
</Wrapper>
)
},
...
]
}
]);