Есть ли способ изменить заголовок страницы с помощью 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} />
// ...

CodePen здесь, но не может обновить заголовок кадра

Если вы осмотрите <head> кадра вы можете увидеть изменения:

Я отвечаю на это, потому что чувствую, что вы могли бы сделать дополнительный шаг, чтобы избежать повторений в ваших компонентах, и вы могли бы просто обновить заголовок из одного места (модуля маршрутизатора).

Обычно я объявляю свои маршруты как массив, но вы можете изменить свою реализацию в зависимости от вашего стиля. так что в основном что-то вроде этого ==>

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";
}, []);
Другие вопросы по тегам