[英]react hook for setting the document title does not work on ssr
我有以下鈎子用於設置最初在服務器上呈現的反應網站的文檔標題:
import { useEffect } from 'react';
export const useDocumentTitle = (title: string): void => {
useEffect(() => {
document.title = title;
}, [title]);
};
這不在服務器上運行,僅在瀏覽器中運行。 有沒有辦法讓這個鈎子在服務器上運行?
document.title
只能應用於client side rendering
,因為您需要訪問 dom 才能更新標題。 在服務器端渲染上,您無權訪問文檔或 window,因此需要為 html 提供每個頁面或路線的元標記
使用服務器端渲染處理document.title
有幾種解決方案
react-helmet
,它可以讓你更好地控制元標記示例用法
const App= ({title, children}) => {
return (
<React.Fragment>
<Helmet>
<title>{title}</title>
</Helmet>
{/* Other logic here */ }
</React.Fragment>
)
}
現在如果你想實現一個通用組件,你可以編寫一個像 HOC 這樣的組件
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
的庫可能過於依賴。 這里的另一個解決方案是編寫自己的組件來處理服務器端呈現,但也允許您在客戶端的文檔中添加標題。在編寫這樣的自定義組件之前,您必須注意您可能需要在同一視圖中處理組件的多個實例。 到 go 使用最簡單的方法,您可以選擇獲取最后一個活動實例來呈現文檔標題
您還必須注意,到目前為止,這里不能替代 serverSide 上的componentWillMount
下面的代碼受@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;
}
}
}
現在我們已經編寫了 class 組件,我們可以按以下方式使用它
const App = () => (
<AddDocumentTitle title={"Title page"}>
{/* Some components here */}
</AddDocumentTitle >
)
現在在服務器renderToStatic
serverSide
這個獲得的標題嵌入到 HTML 頁面模板中,就像我們對 Helmet 所做的那樣
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>
`
}
我希望這些信息對你有幫助
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.