首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >react钩子用于设置文档标题不适用于ssr

问react钩子用于设置文档标题不适用于ssr
EN

Stack Overflow用户
提问于 2020-05-02 06:47:56
回答 1查看 1.1K关注 0票数 1

我有以下钩子用于设置最初在服务器上呈现的react网站的文档标题:

代码语言:javascript
复制
import { useEffect } from 'react';

export const useDocumentTitle = (title: string): void => {
  useEffect(() => {
    document.title = title;
  }, [title]);
};

这不会在服务器上运行,只在浏览器中运行。有没有办法让这个钩子在服务器上运行?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-11 19:04:57

document.title只能应用于client side rendering,因为您需要访问dom才能更新标题。在服务器端呈现时,您无法访问文档或窗口,因此需要为每个页面或路由向html提供元标记。

使用服务器端呈现处理document.title有两种解决方案。

  • you可以使用现有的库(如果您愿意使用一个库),比如允许您更多地控制元tags

的

示例用法

代码语言:javascript
复制
const App= ({title, children}) => {
   return (
    <React.Fragment>
       <Helmet>
          <title>{title}</title>
        </Helmet>
        {/* Other logic here */ }
     </React.Fragment>
   )
}

现在,如果您希望实现一个泛型组件,您可以编写一个临时组件。

代码语言:javascript
复制
const TitleWrapper = ({title, children}) => {
   return (
    <React.Fragment>
       <Helmet>
          <title>{title}</title>
        </Helmet>
        {children}
     </React.Fragment>
   )
}

现在,您可以在组件中使用它,如

代码语言:javascript
复制
const App = () => (
   <TitleWrapper title={"Title page"}>
       {/* Some components here */}
  </TitleWrapper>
)

现在,当你在约会时,你可以把它写成

代码语言:javascript
复制
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>
  `
}

  • Now认为,仅仅为了更新 document.title 的库可能过于依赖。这里的另一个解决方案是编写自己的组件,它处理服务器端呈现,但也允许您在客户端side.

上添加文档中的标题。

在编写这样的自定义组件之前,您必须注意,您可能需要在同一个视图中处理多个组件实例。要使用最简单的方法,可以选择获取最后一个活动实例来呈现文档标题。

您还必须注意,到目前为止,这里没有替代componentWillMount on serverSide的方法。

下面的代码是由@DanAbramov的react-document-title激发的

代码语言:javascript
复制
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;
       }
    }
}

既然我们已经编写了类组件,我们就可以以以下方式使用它了

代码语言:javascript
复制
const App = () => (
   <AddDocumentTitle title={"Title page"}>
       {/* Some components here */}
  </AddDocumentTitle >
)

现在,在serverSide上,您需要调用renderToStatic并将获得的标题嵌入到HTML页面模板中,就像我们使用头盔一样

代码语言:javascript
复制
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>
  `
}

我希望这些信息对你有帮助。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61555514

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档