1. ホーム
  2. javascript

[解決済み] ref コールバックの前に呼び出された componentDidMount

2023-03-02 07:40:51

質問

問題点

反応する ref をインライン関数定義を使って設定しています。

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>

であれば componentDidMount では DOM 参照が設定されていません。

componentDidMount = () => {
    // this.drawerRef is not defined

私の理解では ref コールバックはマウント時に実行されるべきだと理解しています。 console.log ステートメントを追加すると componentDidMount とは の前に を呼び出します。

私が見た他のコードサンプルは、例えば この議論 を見ると、同じような前提で書かれています。 componentDidMount と呼ばれるべきです。 の後に 任意の ref で定義されたコールバックは render であれば、それはさらに の会話で述べられている

<ブロッククオート <ブロッククオート

つまり、componentDidMountは、すべてのrefコールバックが実行された後に起動されるのですね。 実行されるのですね?

はい、そうです。

私が使っているのはreact 15.4.1

他に試したこと

を確認するために ref 関数が呼び出されていることを確認するために、クラス上で次のように定義してみました。

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}

であれば render

<div className="drawer" ref={this.setDrawerRef}>

この場合、コンソールのロギングにより、コールバックが実際に呼び出されていることがわかります。 の後に componentDidMount

どのように解決するのですか?

簡単な答えです。

React は、ref が componentDidMount または componentDidUpdate のフックを使用します。ただし、子プロセスに対してのみ 実際にレンダリングされた .

componentDidMount() {
  // can use any refs here
}

componentDidUpdate() {
  // can use any refs here
}

render() {
  // as long as those refs were rendered!
  return <div ref={/* ... */} />;
}

これは、「Reactは常に をすべて を設定する」という意味ではありません。

では、いくつかの例を見てみましょう。 が実行されない例を見てみましょう。 が設定される例を見てみましょう。


レンダリングされなかった要素に参照は設定されない

React は実際にレンダリングされた要素に対してのみ ref コールバックを呼び出します。 レンダリングから返された .

つまり、もしあなたのコードが以下のようなものであれば

render() {
  if (this.state.isLoading) {
    return <h1>Loading</h1>;
  }

  return <div ref={this._setRef} />;
}

で、最初は this.state.isLoadingtrue であれば ではない 期待する this._setRef が呼び出される前に componentDidMount .

これは、最初のレンダリングで <h1>Loading</h1> を返した場合、React が他の条件下で、ref を添付する必要がある他の何かを返すことを知る方法はありません。また refを設定するものがない。 その <div> 要素が作成されなかったのは render() メソッドがレンダリングしてはいけないと言ったからです。

ですから、この例では componentDidMount だけが発火します。しかし の場合は this.state.loading に変化すると false と表示されます。 this._setRef が最初に付き、次に componentDidUpdate が発射されます。


他の部品に注意する

以下のことに注意してください。 を持つ子供を他のコンポーネントに渡すと、そのコンポーネントは の場合、他のコンポーネントがレンダリングを妨げる(そして問題を引き起こす)何かを行っている可能性があります。

たとえば、これです。

<MyPanel>
  <div ref={this.setRef} />
</MyPanel>

は、もし MyPanelprops.children を出力に含めませんでした。

function MyPanel(props) {
  // ignore props.children
  return <h1>Oops, no refs for you today!</h1>;
}

繰り返しになりますが、バグではありません。 が作成されていないため、Reactがrefを設定することはできません。 .


ネストされた ReactDOM.render()

前のセクションと同様に、refを持つ子を他のコンポーネントに渡すと、そのコンポーネントが何かをしてrefを時間内にアタッチできない可能性があります。

例えば、多分それは、子プロセスを render() を返さず、代わりに ReactDOM.render() をライフサイクルフックで呼び出しています。この例として の例です。 . その例では、レンダリングしています。

<MyModal>
  <div ref={this.setRef} />
</MyModal>

しかし MyModalReactDOM.render() での呼び出し その componentDidUpdate のライフサイクルメソッドです。

componentDidUpdate() {
  ReactDOM.render(this.props.children, this.targetEl);
}

render() {
  return null;
}

React 16以降、このような のトップレベル レンダリング呼び出しは、ツリー全体に対してライフサイクルが実行されるまで遅延されます。 . これは、時間内に添付された refs が表示されない理由を説明するものです。

この問題の解決策は ポータル の代わりに、ネストされた ReactDOM.render を呼び出します。

render() {
  return ReactDOM.createPortal(this.props.children, this.targetEl);
}

このように <div> はレンダリング出力に含まれます。

したがって、この問題が発生した場合は、コンポーネントと参照との間に、子供のレンダリングを遅らせる可能性のあるものがないことを確認する必要があります。

を使用しないでください。 setState を使用しないでください。

を使用していないことを確認してください。 setState を使用して、ref コールバックに ref を格納していないことを確認してください。これは、非同期で "終了" する前だからです。 componentDidMount が先に実行されます。


まだ問題がありますか?

上記のヒントがどれも役に立たない場合、React に問題を報告してください。