かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

React HTML の HEAD 情報を書き換える react-helmet 使おうとしたら警告が出た件

React で <head> の情報を書き換えるのに react-helmet を使った所 Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. という警告が発生してしまいました。

環境

  • "react": "^17.0.2",
  • "react-helmet": "^6.1.0"

react-helmet で発生した警告

import { VFC } from 'react';
import { Helmet } from 'react-helmet';

export const App: VFC = () => {
  return (
    <>
      <Helmet>
        <title>{PAGE_TITLE}</title>
        <script src={SCRIPT} />
      </Helmet>
      <div className="app">{/*略*/}</div>
    </>
  );
};

👇

Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
* Move code with side effects to componentDidMount, and set initial state in the constructor.
Please update the following components: SideEffect(NullComponent)

React 16.3 以降 componentWillMount メソッドは安全でないとして非推奨になっていた

  • 16.3: Introduce aliases for the unsafe lifecycles, UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps, and UNSAFE_componentWillUpdate. (Both the old lifecycle names and the new aliases will work in this release.)
  • A future 16.x release: Enable deprecation warning for componentWillMount, componentWillReceiveProps, and componentWillUpdate. (Both the old lifecycle names and the new aliases will work in this release, but the old names will log a DEV-mode warning.)
  • 17.0: Remove componentWillMount, componentWillReceiveProps, and componentWillUpdate . (Only the new “UNSAFE_” lifecycle names will work from this point forward.)

Here, “unsafe” refers not to security but instead conveys that code using these lifecycles will be more likely to have bugs in future versions of React, especially once async rendering is enabled.
cf. Update on Async Rendering – React Blog

componentWillMount, componentWillReceiveProps, componentWillUpdate は特に非同期レンダリングをするようになると誤用するとバグを発生させやすいので非推奨として、UNSAFE_ の付いたメソッドのエイリアスを作成し React v 17.0 で元のメソッドが削除となっているようです。

react-helmet では内部で使用しているライブラリ(react-side-effect)で UNSAFE_componentWillMount が使われているために、今回の警告が発生しているようでした。
cf. Stop using `UNSAFE_componentWillMount` · Issue #548 · nfl/react-helmet · GitHub

<head> の変更は react-helmet-async を使う

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.
cf. react-helmet-async - npm

React Helmet を fork して作られた react-helmet-async はこの react-side-effect の問題を解決しているようです。そして react-helmet-async は TypeScript なので @types をインストールする必要もなく、Next.js でも使われているようです!(Download数も react-helmet-async の方が多いですね)

react-helmet-async の使い方

react-helmet-async は react-helmet とは少し異なり <Helmet> タグを使用するコンポーネント<HelmetProvider> で囲ってあげる必要があります。
先ほど警告の出ていたコードを修正します。

import { VFC } from 'react';
+ import { Helmet, HelmetProvider } from 'react-helmet-async';
- import { Helmet } from 'react-helmet';

export const App: VFC = () => {
  return (
+   <HelmetProvider>
-   <>
      <Helmet>
        <title>{PAGE_TITLE}</title>
        <script src={SCRIPT} />
      </Helmet>
      <div className="app">{/*略*/}</div>
+   </HelmetProvider>
-   </>
  );
};

これで Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. な警告は出なくなりました!
アプリ全体で使うなら src/index.tsx やアプリのルートになる src/App.tsx でアプリを丸っと囲ってあげれば良さそう。

結論

React で <head> を書き換えるときは react-helmet-async を使おう!
Class Component 時代を通ってこなかったので componentWillMount まわりのアレコレ全然把握してなかったのと、これからも React はどんどん変化していくだろうからちゃんとキャッチアップしておかないとな〜と感じました。

[参考]

Viva!公務員(字幕版)

Viva!公務員(字幕版)

  • ケッコ・ザローネ
Amazon
イタリア映画おもしろいです!