Есть ли способ изменить заголовок страницы с помощью React-Router v4+?

Я ищу способ изменить заголовок страницы, когда React-Router v4+ изменяет местоположение. Я привык слушать действие изменения местоположения в Redux и проверять этот маршрут на объект metaData.

При использовании React-Router v4+ нет списка фиксированных маршрутов. Фактически, различные компоненты вокруг сайта могут использовать Route с той же строкой пути. Это означает, что старый метод, который я использовал, больше не будет работать.

Есть ли способ обновить заголовок страницы, вызывая действия при изменении некоторых основных маршрутов или улучшая лучший способ обновления метаданных сайта?

Ответ 1

В вашем методе componentDidMount() сделайте это для каждой страницы

componentDidMount() {
  document.title = 'Your page title here';
}

Это изменит заголовок вашей страницы, сделайте вышеуказанное для каждого маршрута.

Также, если это больше, чем просто заглавная часть, проверьте ответ-шлем. Это очень аккуратная библиотека для этого, которая также обрабатывает некоторые замечательные крайние случаи.

Ответ 2

Компоненты <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
  }

  /**
   * Here, we use a component prop to render 
   * a component, as specified in route configuration
   */
  render() {
    const PageComponent = this.props.component

    return (
      <PageComponent />
    )
  }
}

export default Page

Обновление 1 августа 2019 года. Это работает только с response-router> = 4.x. Благодаря @supremebeing7

Ответ 3

Используя функциональный компонент на главной странице маршрутизации, вы можете изменять заголовок при каждом изменении маршрута с помощью useEffect.

Например,

const Routes = () => {
    useEffect(() => {
      let title = history.location.pathname
      document.title = title;
    });

    return (
      <Switch>
        <Route path='/a' />
        <Route path='/b' />
        <Route path='/c' />
      </Switch>
    );
}

Ответ 4

В ответ на отличный ответ phen0menon, почему бы не расширить Route вместо React.Component?

import React, { useEffect } from 'react';
import { Route } from 'react-router-dom';
import PropTypes from 'prop-types';

const Page = ({ title, ...rest }) => {
  useEffect(() => {
    document.title = title), []
  };
  return <Route {...rest} />;
};

Page.propTypes = {
  title: PropTypes.string.isRequired,
};

export { Page };

это удалит служебный код, как показано ниже:

// old:
<Route
  exact
  path="/"
  render={props => (
    <Page {...props} component={Index} title="Index Page" />
  )}
/>
// improvement:
<Page 
  exact
  path="/"
  component={Index}
  title="Index Page" />

Обновите другой способ сделать это с помощью хуков:

import { useEffect } from 'react';
/** Hook for changing title */
export const useTitle = title => {
  useEffect(() => {
    title && (document.title = title);
  }, []);
};

Ответ 5

Я немного построил решение 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 }
            }}
          />
        )
      }
    />
  );
}

Это позволило мне обновлять как публичные области с новым названием, так и частные области.

Ответ 6

Вот мое решение, которое почти такое же, как useEffect установка 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> кадра, вы увидите изменение: screenshot

Ответ 7

С небольшой помощью от Шлема:

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>
  )
}