我有以下钩子用于设置最初在服务器上呈现的react网站的文档标题:
import { useEffect } from 'react';
export const useDocumentTitle = (title: string): void => {
useEffect(() => {
document.title = title;
}, [title]);
};这不会在服务器上运行,只在浏览器中运行。有没有办法让这个钩子在服务器上运行?
发布于 2020-05-11 19:04:57
document.title只能应用于client side rendering,因为您需要访问dom才能更新标题。在服务器端呈现时,您无法访问文档或窗口,因此需要为每个页面或路由向html提供元标记。
使用服务器端呈现处理document.title有两种解决方案。
的
示例用法
const App= ({title, children}) => {
return (
<React.Fragment>
<Helmet>
<title>{title}</title>
</Helmet>
{/* Other logic here */ }
</React.Fragment>
)
}现在,如果您希望实现一个泛型组件,您可以编写一个临时组件。
const TitleWrapper = ({title, children}) => {
return (
<React.Fragment>
<Helmet>
<title>{title}</title>
</Helmet>
{children}
</React.Fragment>
)
}现在,您可以在组件中使用它,如
const App = () => (
<TitleWrapper title={"Title page"}>
{/* Some components here */}
</TitleWrapper>
)现在,当你在约会时,你可以把它写成
app.get('/*', (req, res) => {
const app = renderToString(<App />);
const helmet = Helmet.renderStatic();
res.send(formatHTML(app, helmet));
});
app.listen(8000);
function formatHTML(appStr, helmet) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${helmet.title.toString()}
</head>
<body>
<div id="root">
${ appStr }
</div>
<script src="./bundle.js"></script>
</body>
</html>
`
}document.title 的库可能过于依赖。这里的另一个解决方案是编写自己的组件,它处理服务器端呈现,但也允许您在客户端side.上添加文档中的标题。
在编写这样的自定义组件之前,您必须注意,您可能需要在同一个视图中处理多个组件实例。要使用最简单的方法,可以选择获取最后一个活动实例来呈现文档标题。
您还必须注意,到目前为止,这里没有替代componentWillMount on serverSide的方法。
下面的代码是由@DanAbramov的react-document-title激发的
class AddDocumentTitle extends React.Component {
static mountedInstances = []; // keep track of mounted instances. Being a prototype this won't be reinitializd for each instance
static renderStatic() {
// Will be used on serverSide
const inst = AddDocumentTitle.getCurrentActiveInstance();
AddDocumentTitle.mountedInstances.splice(0)// remove all instance since on serverside componentWillUnmount won't run
if(inst) {
return inst.props.title; // return the title props
}
}
static getCurrentActiveInstance() {
const length = AddDocumentTitle.mountedInstances.length;
if (length > 0) {
return AddDocumentTitle.mountedInstances[length - 1];
}
return null;
}
static updateDocumentTitle() {
if (typeof document === 'undefined') {
// for serverside this will be undefined
return;
}
// code that will only run on clientSide
const activeInstance = AddDocumentTitle.getActiveInstance();
if (activeInstance) {
document.title = activeInstance.props.title;
}
}
static defaultProps = {
title: ''
}
constructor(props) {
super(props);
AddDocumentTitle.mountedInstances.push(this); // Add instance to mounted instance
AddDocumentTitle.updateDocumentTitle(); // This will update it on serverSide
}
componentDidUpdate(prevProps) {
if (this.isActive() && prevProps.title !== this.props.title) {
// Update title if this instance is still the active instance and props title changed
AddDocumentTitle.updateDocumentTitle();
}
}
componentWillUnmount() {
// Remove this instance from instance list and update documentTitle according to the now active instance
const index = AddDocumentTitle.mountedInstances.indexOf(this);
AddDocumentTitle.mountedInstances.splice(index, 1);
AddDocumentTitle.updateDocumentTitle();
}
isActive() {
return this === DocumentTitle.getActiveInstance();
}
render() {
if (this.props.children) {
return React.Children.only(this.props.children);
} else {
return null;
}
}
}既然我们已经编写了类组件,我们就可以以以下方式使用它了
const App = () => (
<AddDocumentTitle title={"Title page"}>
{/* Some components here */}
</AddDocumentTitle >
)现在,在serverSide上,您需要调用renderToStatic并将获得的标题嵌入到HTML页面模板中,就像我们使用头盔一样
app.get('/*', (req, res) => {
const app = renderToString(<App />);
const title= AddDocumentTitle.renderStatic();
res.send(formatHTML(app, title));
});
app.listen(8000);
function formatHTML(appStr, title) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${title}
</head>
<body>
<div id="root">
${ appStr }
</div>
<script src="./bundle.js"></script>
</body>
</html>
`
}我希望这些信息对你有帮助。
https://stackoverflow.com/questions/61555514
复制相似问题