LoginSignup
5

More than 5 years have passed since last update.

Reactをdependenciesに含むnpmライブラリを使用するとReact.findDOMNodeがたまに死ぬ

Last updated at Posted at 2015-05-19

React v0.13.3 時点での情報です (findDOMNode実装から0.0.3しか経っていません)
https://facebook.github.io/react/blog/2015/05/08/react-v0.13.3.html

結論

Reactをdependenciesに含めたnpmライブラリを使用するとReact.findDOMNodeが死ぬ可能性がある
※ npmライブラリが独自でReact持っててその中でrenderしてたらアウト

事の発端

現象

コンポーネント内部でReact.findDOMNodeしようとすると、Cannot read property 'firstChild' of undefinedで死ぬ。
マウントタイミングの問題かと思って場所をcomponentDidMountより後ろにずらしてみてもやっぱダメ。

調査してみた

落ちるのはfindComponentRoot

ReactMount.js
findComponentRoot: function(ancestorNode, targetID) {
    var firstChildren = findComponentRootReusableArray;
    var childIndex = 0;

    var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;

    firstChildren[0] = deepestAncestor.firstChild;
...

ここで既にfindComponentRootReusableArrayが空になっている。
ソースを追っていくとfindReactContainerForIDにたどり着きます。

ReactMount.js
findReactContainerForID: function(id) {
    var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
    var container = containersByReactRootID[reactRootID];
...

containersByReactRootIDはコンポーネント初期化の際に親コンポーネントが随時登録されていく場所です。
今回はこの containersByReactRootID が空配列になっていました。
以上の状況は、コンポーネントをrenderしたReactオブジェクトとfindDOMNodeしたReactオブジェクトが違うと発生します。

どうしてこんなことになったのか

原因は昨晩自分で公開したreact-providerでした。
このライブラリはReactとreact-toolsとlodashに依存しているので、軽い気持ちで取り敢えず全部入れとけ、とpackage.jsonのdependenciesにこう書きました。

package.json
 "dependencies": {
    "lodash": "~3.8.0",
    "react": "^0.13.3",
    "react-tools": "^0.13.3"
  },

npmの依存解決の仕組みをよく知らなかったので、既に親のnode_modulesに入っているものはスキップして、そっちを使ってくれるだろう 程度に考えていたのですが実際は、node_modulesに必ずdependenciesの内容がインストールされ、ライブラリはそこから依存するモジュールを呼び出すようです。

つまりここにReact本体が含まれていたことにより、
アプリケーション部分が使用しているReactオブジェクトとライブラリの使用しているReactオブジェクトが違う
という先ほどのエラーを生む状況が発生しました。

ちなみにreact-providerは中でReact.renderをおもっくそ走らせているので完全にアウトです。
本当にありがとうございました。

直し方

ライブラリのpackage.jsonのdependenciesからreactを削除してnode_modulesを消し、
npm installをやり直せば綺麗に動くようになります。

こうすると親のnode_modulesからreactを呼び出して動作してくれるようになります。
要はdependenciesにさえ書かなければ大丈夫。

終わりに

そもそもライブラリのdependenciesにreactを含めて公開するのが作法として間違っていたような気がします。(知っている方がいらっしゃったら教えて下さい)
万が一 Cannot read property 'firstChild' of undefined に遭遇した時はこの記事のことを思い出してやって下さい。

補足

Q. プルリク送らないの?
A. たぶんこれ解決するの結構難しいし、しなくても良いやつ

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5