项目作者: staylor

项目描述 :
Thread-safe Helmet for React 16+ and friends
高级语言: JavaScript
项目地址: git://github.com/staylor/react-helmet-async.git
创建时间: 2018-01-02T08:08:47Z
项目社区:https://github.com/staylor/react-helmet-async

开源协议:Apache License 2.0

下载


react-helmet-async

CircleCI

Announcement post on Times Open blog

This package is a fork of React Helmet.
<Helmet> usage is synonymous, but server and client now requires <HelmetProvider> to encapsulate state per request.

react-helmet relies on react-side-effect, which is not thread-safe. If you are doing anything asynchronous on the server, you need Helmet to encapsulate data on a per-request basis, this package does just that.

Usage

New is 1.0.0: No more default export! import { Helmet } from 'react-helmet-async'

The main way that this package differs from react-helmet is that it requires using a Provider to encapsulate Helmet state for your React tree. If you use libraries like Redux or Apollo, you are already familiar with this paradigm:

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { Helmet, HelmetProvider } from 'react-helmet-async';
  4. const app = (
  5. <HelmetProvider>
  6. <App>
  7. <Helmet>
  8. <title>Hello World</title>
  9. <link rel="canonical" href="https://www.tacobell.com/" />
  10. </Helmet>
  11. <h1>Hello World</h1>
  12. </App>
  13. </HelmetProvider>
  14. );
  15. ReactDOM.hydrate(
  16. app,
  17. document.getElementById(‘app’)
  18. );

On the server, we will no longer use static methods to extract state. react-side-effect
exposed a .rewind() method, which Helmet used when calling Helmet.renderStatic(). Instead, we are going
to pass a context prop to HelmetProvider, which will hold our state specific to each request.

  1. import React from 'react';
  2. import { renderToString } from 'react-dom/server';
  3. import { Helmet, HelmetProvider } from 'react-helmet-async';
  4. const helmetContext = {};
  5. const app = (
  6. <HelmetProvider context={helmetContext}>
  7. <App>
  8. <Helmet>
  9. <title>Hello World</title>
  10. <link rel="canonical" href="https://www.tacobell.com/" />
  11. </Helmet>
  12. <h1>Hello World</h1>
  13. </App>
  14. </HelmetProvider>
  15. );
  16. const html = renderToString(app);
  17. const { helmet } = helmetContext;
  18. // helmet.title.toString() etc…

Streams

This package only works with streaming if your <head> data is output outside of renderToNodeStream().
This is possible if your data hydration method already parses your React tree. Example:

  1. import through from 'through';
  2. import { renderToNodeStream } from 'react-dom/server';
  3. import { getDataFromTree } from 'react-apollo';
  4. import { Helmet, HelmetProvider } from 'react-helmet-async';
  5. import template from 'server/template';
  6. const helmetContext = {};
  7. const app = (
  8. <HelmetProvider context={helmetContext}>
  9. <App>
  10. <Helmet>
  11. <title>Hello World</title>
  12. <link rel="canonical" href="https://www.tacobell.com/" />
  13. </Helmet>
  14. <h1>Hello World</h1>
  15. </App>
  16. </HelmetProvider>
  17. );
  18. await getDataFromTree(app);
  19. const [header, footer] = template({
  20. helmet: helmetContext.helmet,
  21. });
  22. res.status(200);
  23. res.write(header);
  24. renderToNodeStream(app)
  25. .pipe(
  26. through(
  27. function write(data) {
  28. this.queue(data);
  29. },
  30. function end() {
  31. this.queue(footer);
  32. this.queue(null);
  33. }
  34. )
  35. )
  36. .pipe(res);

Usage in Jest

While testing in using jest, if there is a need to emulate SSR, the following string is required to have the test behave the way they are expected to.

  1. import { HelmetProvider } from 'react-helmet-async';
  2. HelmetProvider.canUseDOM = false;

Prioritizing tags for SEO

It is understood that in some cases for SEO, certain tags should appear earlier in the HEAD. Using the prioritizeSeoTags flag on any <Helmet> component allows the server render of react-helmet-async to expose a method for prioritizing relevant SEO tags.

In the component:

  1. <Helmet prioritizeSeoTags>
  2. <title>A fancy webpage</title>
  3. <link rel="notImportant" href="https://www.chipotle.com" />
  4. <meta name="whatever" value="notImportant" />
  5. <link rel="canonical" href="https://www.tacobell.com" />
  6. <meta property="og:title" content="A very important title"/>
  7. </Helmet>

In your server template:

  1. <html>
  2. <head>
  3. ${helmet.title.toString()}
  4. ${helmet.priority.toString()}
  5. ${helmet.meta.toString()}
  6. ${helmet.link.toString()}
  7. ${helmet.script.toString()}
  8. </head>
  9. ...
  10. </html>

Will result in:

  1. <html>
  2. <head>
  3. <title>A fancy webpage</title>
  4. <meta property="og:title" content="A very important title"/>
  5. <link rel="canonical" href="https://www.tacobell.com" />
  6. <meta name="whatever" value="notImportant" />
  7. <link rel="notImportant" href="https://www.chipotle.com" />
  8. </head>
  9. ...
  10. </html>

A list of prioritized tags and attributes can be found in constants.ts.

Usage without Context

You can optionally use <Helmet> outside a context by manually creating a stateful HelmetData instance, and passing that stateful object to each <Helmet> instance:

  1. import React from 'react';
  2. import { renderToString } from 'react-dom/server';
  3. import { Helmet, HelmetProvider, HelmetData } from 'react-helmet-async';
  4. const helmetData = new HelmetData({});
  5. const app = (
  6. <App>
  7. <Helmet helmetData={helmetData}>
  8. <title>Hello World</title>
  9. <link rel="canonical" href="https://www.tacobell.com/" />
  10. </Helmet>
  11. <h1>Hello World</h1>
  12. </App>
  13. );
  14. const html = renderToString(app);
  15. const { helmet } = helmetData.context;

License

Licensed under the Apache 2.0 License, Copyright © 2018 Scott Taylor