{
componentDidUpdate() {
//なにか更新があったときに呼び出される
const body = document.body
this.setState({isFixedBottom: body.scrollHeight < window.innerHeight})
}
}
```
いや〜、出来た出来・・うん?
'componentDidUpdate()'が呼びされない・・だと・・・
なぜだぁ・・・なぜなんだ・・・
フッターそのものは更新されないぞ
その通りである、実際に更新されるのはページ内の他のコンポーネントなので、フッターそのものは更新されないのである。
困った。これは困った。
フッターではなくてページ単位でフックするようにしたらいいのだと思うけど、コンポーネント間のやり取りが出来てしまって管理が大変になるし、
毎回ページにそれ関係のコードを追加するのがめんどくさいぞ。
いったいどうしたらいいんだ・・・
## ProxyをつかってダイレクトにBodyの高さを監視する
じゃあさぁ・・・bodyタグの高さの変更をフッターコンポーネント内で監視するようにしたらいいんじゃない?
というわけで早速調べてみると、JavaScriptにProxyというものがあってそれがぴったり今回の問題に使えそうであった。
[Proxy](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
```js
export class Footer extends React.Component {
constructor(props: Props) {
super(props)
const bodyProxy = new Proxy(document.body, {
set: (target: HTMLElement, name: string | number | symbol, value: any): boolean => {
if ('scrollHeight' !== name) {
return true
}
// 現在の状態と異なっていたときに変更するようにしたほうがいいけど簡略のためにこうしている
this.setState({isFixedBottom: document.body.scrollHeight < window.innerHeight})
return true
}
})
document.body = bodyProxy // エラー
}
}
```
が、ダメ・・・
Proxy経由で値を変更しないと設定したハンドラーは実行されないので、なんとかしてdocumentにあるbodyをそれと差し替えないといけない。
上のコードのようにReactでは直接値を書き換えることは禁止されているので、どうしたらいいんだ・・・。
## DOMのイベントハンドラーに都合のいいイベントありますか?
ないです!
`Document.onvisibilitychange`はウィンドウやタブ単位の可視性が変わったときに発行されるイベントで今回とは異なる。
`Window:afterprintイベント`もドキュメントが描画されから終わった後に発行されるので、これも異なる。
JSではうまく検知できない問題?
そんなことない!
#### MutationObserver!!
jsにはMutationObserverというDomツリーの変更を監視できるインターフェースが用意されている。
[MutationObserver ドキュメント](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
Proxyでは生成したインスタンス経由で値を変更しないとコールバックが呼び出されなかったので断念したが、これを使えばできる!
それで以下のようなコードを書くと、
```js
export class Footer extends React.Component {
constructor(props: Props) {
super(props)
this.state = {
isFixedBottom: false
}
let config = {
childList: true,
subtree: true,
} as MutationObserverInit
const callback = (_mutationList: MutationRecord[], _observer: MutationObserver): void => {
const lowBodyHeight = document.body.scrollHeight < window.innerHeight
if (this.state.isFixedBottom !== lowBodyHeight) {
this.setState({
isFixedBottom: lowBodyHeight
})
}
}
let observer = new MutationObserver(callback)
observer.observe(document.body, config)
}
// ...
```
無事、目的を達成することが出来る。
`observer.observe(document.body, config)`の部分で監視を始めている。
第一引数に監視対象を、第二引数に監視内容を渡す。
今回は子要素も含めたノードで子の追加/削除があったときにコールバックを呼び出すように設定してある。
これで他のコンポーネントの更新があったかが擬似的にわかるようになったので、
あとはそれに合わせてフッターを画面下部に固定するかしないかを決めるだけであっさりと出来上がり。
終わり
0 件のコメント:
コメントを投稿