我正在寻找一种在 React-Router v4+ 更改位置时修改页面标题的方法。我曾经在 Redux 中侦听位置更改操作,并根据metaData
对象检查该路由。
使用 React-Router v4+ 时,没有固定的路由列表。事实上,站点周围的各种组件都可以使用Route
相同的路径字符串。这意味着我使用的旧方法将不再有效。
有没有一种方法可以通过在某些主要路线发生变化时调用操作来更新页面标题,或者是否有更好的方法来更新站点的元数据?
我正在寻找一种在 React-Router v4+ 更改位置时修改页面标题的方法。我曾经在 Redux 中侦听位置更改操作,并根据metaData
对象检查该路由。
使用 React-Router v4+ 时,没有固定的路由列表。事实上,站点周围的各种组件都可以使用Route
相同的路径字符串。这意味着我使用的旧方法将不再有效。
有没有一种方法可以通过在某些主要路线发生变化时调用操作来更新页面标题,或者是否有更好的方法来更新站点的元数据?
<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
2019 年 8 月 1 日更新。这仅适用于 react-router >= 4.x。感谢@supremebeing7
使用React Hooks更新答案:
您可以使用以下组件指定任何路线的标题,该组件是使用useEffect
.
import { useEffect } from "react";
const Page = (props) => {
useEffect(() => {
document.title = props.title || "";
}, [props.title]);
return props.children;
};
export default Page;
然后Page
在render
路线的道具中使用:
<Route
path="/about"
render={(props) => (
<Page title="Index">
<Index {...props} />
</Page>
)}
/>
<Route
path="/profile"
render={(props) => (
<Page title="Profile">
<Profile {...props} />
</Page>
)}
/>
在您的componentDidMount()
方法中,为每一页执行此操作
componentDidMount() {
document.title = 'Your page title here';
}
这将更改您的页面标题,为每条路线执行上述操作。
此外,如果它不仅仅是标题部分,请检查react-helmet它是一个非常简洁的库,并且还处理了一些不错的边缘情况。
从phen0menon的优秀答案中汲取灵感,为什么不扩展Route
而不是React.Component
?
import React, { useEffect } from 'react';
import { Route } from 'react-router-dom';
import PropTypes from 'prop-types';
export const Page = ({ title, ...rest }) => {
useEffect(() => {
document.title = title;
}, [title]);
return <Route {...rest} />;
};
这将删除开销代码,如下所示:
// 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(() => {
const oldTitle = document.title;
title && (document.title = title);
// following line is optional, but will reset title when component unmounts
return () => document.title = oldTitle;
}, [title]);
};
使用主路由页面上的功能组件,您可以使用useEffect在每次路由更改时更改标题。
例如,
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 解决方案上做了一些构建,最终得到以下结果:
2020 年 1 月更新:我现在也将我的组件更新为在 Typescript 中:
2021 年 8 月更新:我在 TypeScript 中添加了我的私人路线
import React, { FunctionComponent, useEffect } from 'react';
import { Route, RouteProps } from 'react-router-dom';
interface IPageProps extends RouteProps {
title: string;
}
const Page: FunctionComponent<IPageProps> = props => {
useEffect(() => {
document.title = "Website name | " + props.title;
});
const { title, ...rest } = props;
return <Route {...rest} />;
};
export default Page;
更新:我的 Page.jsx 组件现在是一个功能组件并带有 useEffect 钩子:
import React, { useEffect } from 'react';
import { Route } from 'react-router-dom';
const Page = (props) => {
useEffect(() => {
document.title = "Website name | " + props.title;
});
const { title, ...rest } = props;
return <Route {...rest} />;
}
export default Page;
您可以在下面找到我的初始解决方案:
// 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 }
}}
/>
)
}
/>
);
}
TypeScript 中的私有路由:
export const PrivateRoute = ({ Component, ...rest }: IRouteProps): JSX.Element => {
return (
<Page
{...rest}
render={(props) =>
userIsAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: Paths.login,
state: { from: props.location },
}}
/>
)
}
/>
);
};
这使我能够使用新标题更新公共区域和更新私人区域。
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} />
// ...
在 Helmet 的帮助下:
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>
)
}
您也可以使用该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中的示例(在新窗口中打开结果以查看标题)
我正在回答这个问题,因为我觉得您可以采取额外的步骤来避免组件中的重复,并且您可以从一个地方(路由器模块)更新标题。
我通常将我的路线声明为一个数组,但您可以根据您的风格更改您的实现。所以基本上是这样的==>
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 = `<Website Name> |
${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 的解决方案。
请使用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>
...
</>
)
}
}
Dan Abramov(Redux 的创建者和 React 团队的现任成员)创建了一个用于设置标题的组件,该组件也适用于新版本的 React Router。它非常易于使用,您可以在这里阅读:
https://github.com/gaearon/react-document-title
例如:
<DocumentTitle title='My Web App'>