Есть ли способ изменить заголовок страницы с помощью React-Router v4+?
Я ищу способ изменить заголовок страницы, когда React-Router v4+ меняет местоположение. Я привык слушать действие изменения местоположения в Redux и проверять этот маршрут по metaData
объект.
При использовании React-Router v4+ список фиксированных маршрутов отсутствует. Фактически, различные компоненты вокруг сайта могут использовать Route
с той же строкой пути. Это означает, что старый метод, который я использовал, больше не будет работать.
Есть ли способ, которым я могу обновить заголовок страницы, вызывая действия при изменении определенных основных маршрутов, или есть лучший способ обновить метаданные сайта?
13 ответов
В вашем componentDidMount()
метод сделать это для каждой страницы
componentDidMount() {
document.title = 'Your page title here';
}
Это изменит заголовок вашей страницы, сделайте вышеупомянутое для каждого маршрута.
Кроме того, если это больше, чем просто заглавная часть, проверьте ответ-шлем. Это очень аккуратная библиотека для этого, которая также обрабатывает некоторые замечательные крайние случаи.
<Route />
компоненты имеют свойство рендера. Таким образом, вы можете изменить заголовок страницы при изменении местоположения, объявив ваши маршруты следующим образом:
<Route
exact
path="/"
render={props => (
<Page {...props} component={Index} title="Index Page" />
)}
/>
<Route
path="/about"
render={props => (
<Page {...props} component={About} title="About Page" />
)}
/>
В Page
Компонент вы можете установить название маршрута:
import React from "react"
/*
* Component which serves the purpose of a "root route component".
*/
class Page extends React.Component {
/**
* Here, we define a react lifecycle method that gets executed each time
* our component is mounted to the DOM, which is exactly what we want in this case
*/
componentDidMount() {
document.title = this.props.title
}
render() {
const PageComponent = this.props.component
return (
<PageComponent />
)
}
}
export default Page
В ответ на отличный ответ phen0menon, почему бы не расширить Route вместо React.Component?
import React from 'react';
import { Route } from 'react-router-dom';
class Page extends Route {
componentDidMount() {
document.title = this.props.title;
}
render() {
const { title, ...rest } = this.props;
return <Route {...rest} />;
}
}
export default Page;
это удалит служебный код, как показано ниже:
// old:
<Route
exact
path="/"
render={props => (
<Page {...props} component={Index} title="Index Page" />
)}
/>
// improvement:
<Route
exact
path="/"
component={Index}
title="Index Page" />
Используя функциональный компонент на главной странице маршрутизации, вы можете изменять заголовок при каждом изменении маршрута.
Например,
const Routes = () => {
useEffect(() => {
let title = history.location.pathname
document.title = title;
});
return (
<Switch>
<Route path='/a' />
<Route path='/b' />
<Route path='/c' />
</Switch>
);
}
Я немного построил решение Thierry Prosts и получил следующее:
// Page.jsx
import React from 'react';
import { Route } from 'react-router-dom';
class Page extends Route {
componentDidMount() {
document.title = "Website name | " + this.props.title;
}
componentDidUpdate() {
document.title = "Website name | " + this.props.title;
}
render() {
const { title, ...rest } = this.props;
return <Route {...rest} />;
}
}
export default Page;
И моя реализация Router выглядела так:
// App.js / Index.js
<Router>
<App>
<Switch>
<Page path="/" component={Index} title="Index" />
<PrivateRoute path="/secure" component={SecurePage} title="Secure" />
</Switch>
</App>
</Router>
Настройка частного маршрута:
// PrivateRoute
function PrivateRoute({ component: Component, ...rest }) {
return (
<Page
{...rest}
render={props =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/",
state: { from: props.location }
}}
/>
)
}
/>
);
}
Это позволило мне обновлять как публичные области с новым названием, так и частные области.
С небольшой помощью от Шлема:
import React from 'react'
import Helmet from 'react-helmet'
import { Route, BrowserRouter, Switch } from 'react-router-dom'
function RouteWithTitle({ title, ...props }) {
return (
<>
<Helmet>
<title>{title}</title>
</Helmet>
<Route {...props} />
</>
)
}
export default function Routing() {
return (
<BrowserRouter>
<Switch>
<RouteWithTitle title="Hello world" exact={true} path="/" component={Home} />
</Switch>
</BrowserRouter>
)
}
Вот мое решение, которое почти так же, как просто настройка document.title
но используя useEffect
/**
* Update the document title with provided string
* @param titleOrFn can be a String or a function.
* @param deps? if provided, the title will be updated when one of these values changes
*/
function useTitle(titleOrFn, ...deps) {
useEffect(
() => {
document.title = isFunction(titleOrFn) ? titleOrFn() : titleOrFn;
},
[...deps]
);
}
Это имеет преимущество в том, чтобы перерисовать только если вы предоставили deps
менять. Никогда не повторять
const Home = () => {
useTitle('Home');
return (
<div>
<h1>Home</h1>
<p>This is the Home Page</p>
</div>
);
}
Рендеринг только если мой userId
изменения:
const UserProfile = ({ match }) => {
const userId = match.params.userId;
useTitle(() => `Profile of ${userId}`, [userId]);
return (
<div>
<h1>User page</h1>
<p>
This is the user page of user <span>{userId}</span>
</p>
</div>
);
};
// ... in route definitions
<Route path="/user/:userId" component={UserProfile} />
// ...
Я отвечаю на это, потому что чувствую, что вы могли бы сделать дополнительный шаг, чтобы избежать повторений в ваших компонентах, и вы могли бы просто обновить заголовок из одного места (модуля маршрутизатора).
Обычно я объявляю свои маршруты как массив, но вы можете изменить свою реализацию в зависимости от вашего стиля. так что в основном что-то вроде этого ==>
import {useLocation} from "react-router-dom";
const allRoutes = [
{
path: "/talkers",
component: <Talkers />,
type: "welcome",
exact: true,
},
{
path: "/signup",
component: <SignupPage />,
type: "onboarding",
exact: true,
},
]
const appRouter = () => {
const theLocation = useLocation();
const currentLocation = theLocation.pathname.split("/")[1];
React.useEffect(() => {
document.title = `Grade |
${currentLocation[0].toUpperCase()}${currentLocation.slice(1,)}`
}, [currentLocation])
return (
<Switch>
{allRoutes.map((route, index) =>
<Route key={route.key} path={route.path} exact={route.exact} />}
</Switch>
)
}
Другой подход - объявить заголовок уже в каждом из
allRoutes
объект и имеет здесь что-то вроде решения @Denis Skiba.
Вы также можете пойти с render
метод
const routes = [
{
path: "/main",
component: MainPage,
title: "Main Page",
exact: true
},
{
path: "/about",
component: AboutPage,
title: "About Page"
},
{
path: "/titlessPage",
component: TitlessPage
}
];
const Routes = props => {
return routes.map((route, idx) => {
const { path, exact, component, title } = route;
return (
<Route
path={path}
exact={exact}
render={() => {
document.title = title ? title : "Unknown title";
console.log(document.title);
return route.component;
}}
/>
);
});
};
пример в codeandbox (открыть результат в новом окне, чтобы увидеть заголовок)
Я задал вопрос «как правильно установить заголовок» в официальном репозитории GitHub «react router dom» . Они ответили : «Маршрутизатор этого не делает. Вам нужна библиотека типа реактивного шлема ».
Если вы хотите изменить заголовок в руководстве по реагированию на маршрутизатор, вам просто нужно
npm install react-helmet
после этого перейдите на страницу dom вашего реагирующего маршрутизатора (например, contact.jsx ) и добавьте такой код
import { Helmet } from "react-helmet";
export default function Contact() {
const { contact } = useLoaderData();
return (
<div id="contact">
<Helmet>
<title>{ contact.first??'Новый контакт' }</title>
</Helmet>
И в марте 2023 года это работает корректно .
Пожалуйста, используйте реактивный шлем. Я хотел привести пример машинописного текста:
import { Helmet } from 'react-helmet';
const Component1Title = 'All possible elements of the <head> can be changed using Helmet!';
const Component1Description = 'No only title, description etc. too!';
class Component1 extends React.Component<Component1Props, Component1State> {
render () {
return (
<>
<Helmet>
<title>{ Component1Title }</title>
<meta name="description" content={Component1Description} />
</Helmet>
...
</>
)
}
}
Узнайте больше: https://github.com/nfl/react-helmet
Дэн Абрамов (создатель Redux и текущий член команды React) создал компонент для установки заголовка, который также работает с новыми версиями React Router. Его очень легко использовать, и вы можете прочитать об этом здесь:
https://github.com/gaearon/react-document-title
Например:
<DocumentTitle title='My Web App'>
Если вы просто хотите отобразить заголовок компонента, приведенный ниже пример кода также подойдет:
useEffect(() =>
{
document.title = "My page title";
}, []);