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(this)するとfirstChildが見つからないつって落ちるんだけど…
— morisuke (@morisuke_tec) 2015, 5月 19
現象
コンポーネント内部でReact.findDOMNode
しようとすると、Cannot read property 'firstChild' of undefined
で死ぬ。
マウントタイミングの問題かと思って場所をcomponentDidMount
より後ろにずらしてみてもやっぱダメ。
調査してみた
落ちるのはfindComponentRoot。
findComponentRoot: function(ancestorNode, targetID) {
var firstChildren = findComponentRootReusableArray;
var childIndex = 0;
var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;
firstChildren[0] = deepestAncestor.firstChild;
...
ここで既にfindComponentRootReusableArrayが空になっている。
ソースを追っていくとfindReactContainerForIDにたどり着きます。
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にこう書きました。
"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. たぶんこれ解決するの結構難しいし、しなくても良いやつ