2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

componentDidMountでDOMにアクセスできない件

Last updated at Posted at 2019-05-13

まえがき

この記事は根本的な部分については未解決の記事になります。
原因は解明できてませんが、こういう現象があったという覚書きです。

環境

next @7.0.1
react @16.5.2
redux @4.0.0
react-redux @5.0.7
recompose @0.30.0

  • nextでSSRしている
  • jsonで保持しているデータをfetchしてページを生成

やりたかったこと

ハッシュ付きのURLでアクセスした際にアンカーが作動して当該IDの箇所に画面が移動する

要は http://foo.bar#bazz が効くようにしたい。

発生した問題

1.普通にIDを指定して飛ばして見た

上手く遷移せず。
おそらくDOMが生成される前に飛ぼうとして、飛び先が見つかってないのかな?

2.componentDidMountでハッシュを見て処理してみる

lifecycle({
  componentDidMount() {
    if (location.hash) {
      const elem = document.getElementById(location.hash.slice(1))
      if (elem) {
        elem.scrollIntoView({
          behavior: 'auto',
        })
      }
    }
  }
})

動かない。どうして...
componentDidMountなんだからこのタイミングでDOM生成されていて、elemも見つかるはずじゃないのかな?

3.とりあえずsetTimeoutを入れてみる

lifecycle({
  componentDidMount() {
    if (location.hash) {
      setTimeout(() => {
        const elem = document.getElementById(location.hash.slice(1))
        if (elem) {
          elem.scrollIntoView({
            behavior: 'auto',
          })
        }
      }, 1000)
    })
  }
})

動いた。どうして...
componentDidMountから1000msに発火するように調整。なぜか動く。
ちなみに100msとかにすると動かない。
この1000msの間に何があるのだ...

4.onload eventにしてみる

lifecycle({
  componentWillMount() {
    this.props.onBeforeMount()
  },
  componentDidMount() {
    if (location.hash) {
      window.addEventListener('load', () => {
        const elem = document.getElementById(location.hash.slice(1))
        if (elem) {
          elem.scrollIntoView({
            behavior: 'auto',
          })
        }
      })
    }
  }
})

これは動かない。
やってる方もよくわかってないけどcomponentDidMountのタイミングでonloadイベントを入れてみた。
onloadって全て読み込みが終わったあとに発火するのではないの...?どうしてelemが取得できないんだろう。

5.最終的にこうなった

lifecycle({
  componentDidMount() {
    if (location.hash) {
      window.addEventListener('load', () => {
        setTimeout(() => {
          const elem = document.getElementById(location.hash.slice(1))
          if (elem) {
            elem.scrollIntoView({
              behavior: 'auto',
            })
          }
        }, 0)
      })
    }
  }
})

もはや意味がわからない。
componentDidMountのタイミングでonloadイベントとしてsetTimeout(fn, 0)の関数を設定すると動く。

6.どうしてこうなった?

結果をまとめてみる。

設定 結果
componentDidMountのみ 動かない
setTimeout 1000ms 動く
setTimeout 0ms 動かない
onload 動かない
onload + setTimeout 0ms 動く

よくわからない点がいくつか。

  • componentDidMountだけで動かないのはなぜ?コンポーネントがマウントされたあとに発火するから動くはずなのでは?
  • onloadで動かないのはなぜ?全ての要素が(ry
  • setTimeout(fn, 0)はなんとなく後述のような理由で意味があるのかな?

setTimeout(fn, 0)について

setTimeoutは4msの遅延が発生する
setTimeoutは他処理が終わるまでスタックされる

参考
https://developer.mozilla.org/ja/docs/Web/API/WindowTimers/setTimeout#Reasons_for_delays_longer_than_specified

最後にsetTimeout0msを入れて動いたのはどっちの仕様の影響なんだろう?
どちらにしても、onloadから4msの間に何かあるんだろうなぁ...?

まとめ

世の中には不思議なことがいっぱいあるんだなぁ(
こういう根本的な問題が発生した時に、やっぱり仕様を読めた方が良いよなぁと思うので英語を勉強しようと思いましたまる。

2
0
0

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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?