有没有办法使用React-Router v4 +修改页面标题?

时间:2018-09-21 16:30:00

标签: reactjs react-router react-router-v4 page-title connected-react-router

我正在寻找一种当React-Router v4 +更改位置时修改页面标题的方法。我曾经在Redux中监听位置更改操作,然后根据metaData对象检查该路由。

使用React-Router v4 +时,没有固定的路由列表。实际上,站点周围的各个组件都可以将Route与相同的路径字符串一起使用。这意味着我使用的旧方法将不再起作用。

当某些主要路线发生变化时,是否可以通过调用操作来更新页面标题?或者有更好的更好的方法来更新网站的元数据吗?

11 个答案:

答案 0 :(得分:16)

<Route />组件具有render属性。因此,当您更改位置时,可以通过声明路线来修改页面标题:

<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 :(得分:9)

在您的componentDidMount()方法中,对每个页面执行此操作

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

这将更改您的页面标题,对每条路线进行上述操作。

如果仅是标题部分,还请检查react-helmet,这是一个非常整洁的库,并且可以处理一些不错的边缘情况。

答案 2 :(得分:3)

您还可以使用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;
       }}
     />
   );
 });
};

codesandbox处的示例(在新窗口中打开结果以查看标题)

答案 3 :(得分:1)

从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" />

答案 4 :(得分:1)

我在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;

我的路由器实现如下所示:

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

这使我既可以使用新标题更新公共区域,也可以使用私有区域进行更新。

答案 5 :(得分:1)

在头盔的帮助下

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

答案 6 :(得分:1)

这是我的解决方案,与简单地设置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 here but cannot update frame title

如果检查框架的<head>,则可以看到更改: screenshot

答案 7 :(得分:0)

使用主要路线页面上的功能组件,您可以在每次路线更改时更改标题。

例如,

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

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

答案 8 :(得分:0)

请使用react-helmet。我想举一个打字稿的例子:

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#readme

答案 9 :(得分:0)

Dan Abramov(Redux的创建者,React团队的当前成员)创建了一个用于设置标题的组件,该组件也可以与新版本的React Router一起使用。 它非常易于使用,您可以在此处阅读:

https://github.com/gaearon/react-document-title

例如:

<DocumentTitle title='My Web App'>

答案 10 :(得分:0)

我之所以这样回答,是因为我觉得您可以采取进一步的措施来避免组件中的重复,并且您可以从一个位置(路由器的模块)更新标题。

我通常将路由声明为数组,但是您可以根据自己的样式更改实现。所以基本上像这样==>

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的解决方案。