なにこれ

react-cytoscapeのようなwindowオブジェクトを前提としたライブラリをGatsbyで使おうとすると、gatsby developのときは正常動作するにもかかわらず、ビルド時にだけwindow is not definedエラーがでます。今回はその対処法を備忘録として残しておきます。

原因と対処法

Gatsbyビルド時は、windowdocumentといったブラウザのグローバルオブジェクトを参照しているとエラーになります。 この場合、それらがundefinedかチェックが必要です。

// (Before) 以下でビルド時にエラーが出る場合は...
const module = require("module") 

// (After 1) このようにwindowオブジェクトの存在チェックをすることでエラーを免れます
if (typeof window !== `undefined`) {
  const module = require("module")
}

// (After 2) このような形でもOKです
const module = typeof window !== `undefined` ? require("module") : null

またwebpackの設定で、ビルドエラーになるライブラリをダミーに置き換えることで、エラーを免れます。

exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
  if (stage === 'build-html') {
    actions.setWebpackConfig({
      module: {
        rules: [
          {
            test: /react-cytoscapejs/,
            use: loaders.null(),
          },
        ],
      },
    })
  }
}

さらに、そのライブラリを使った処理をReactコンポーネントに記述している場合は、componentDidMountuseEffectに移しましょう。そうすることで、その処理がブラウザでのみ実行されるようになるのでビルド時のエラーを免れます。

import CytoscapeComponent from 'react-cytoscapejs'

class PostRelationSection extends React.Component {

  /*--中略--*/

  componentDidMount() {
    const { posts, allImage } = this.props
    const nodes = createPostNode({ posts, allImage })
    nodes.push(CYTOSCAPE_ZOOM_UP_ELEMENT)
    nodes.push(CYTOSCAPE_ZOOM_DOWN_ELEMENT)

    const edges = createPostEdges({posts})

    const cytoscapeElements = CytoscapeComponent.normalizeElements({ nodes, edges })

    this.setState({ cytoscapeElements })
  }

  /*--中略--*/
}

参考

Takumon
SIer's tech blog powered by Gatsby

Takumon Blog