ReactJS-要素の高さを取得


121

Reactが要素をレンダリングした後、要素の高さを取得するにはどうすればよいですか?

HTML

<div id="container">
<!-- This element's contents will be replaced with your component. -->
<p>
jnknwqkjnkj<br>
jhiwhiw (this is 36px height)
</p>
</div>

ReactJS

var DivSize = React.createClass({

  render: function() {
    let elHeight = document.getElementById('container').clientHeight
    return <div className="test">Size: <b>{elHeight}px</b> but it should be 18px after the render</div>;
  }
});

ReactDOM.render(
  <DivSize />,
  document.getElementById('container')
);

結果

Size: 36px but it should be 18px after the render

レンダリング前のコンテナの高さ(36px)を計算しています。レンダリング後の高さを取得したい。この場合、正しい結果は18pxになります。jsfiddle


1
これは反応の質問ではなく、JavaScriptとDOMの質問です。要素の最終的な高さを見つけるために使用する必要があるDOMイベントを把握する必要があります。イベントハンドラーでは、を使用setStateして高さの値を状態変数に割り当てることができます。
mostruash 2016

回答:


29

このフィドルを参照してください(実際に更新されます)

componentDidMountrenderメソッドの後に実行されるにフックする必要があります。そこで、要素の実際の高さを取得します。

var DivSize = React.createClass({
    getInitialState() {
    return { state: 0 };
  },

  componentDidMount() {
    const height = document.getElementById('container').clientHeight;
    this.setState({ height });
  },

  render: function() {
    return (
        <div className="test">
        Size: <b>{this.state.height}px</b> but it should be 18px after the render
      </div>
    );
  }
});

ReactDOM.render(
  <DivSize />,
  document.getElementById('container')
);
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>

<div id="container">
<p>
jnknwqkjnkj<br>
jhiwhiw (this is 36px height)
</p>
    <!-- This element's contents will be replaced with your component. -->
</div>

15
正しくありません。以下のPaul Vincent Beigangの回答を参照してください
ekatz

1
私見では、コンポーネントがそのコンテナの名前を知っているべきではない
アンドレス・

156

以下は、refを使用した最新のES6の例です。

要素の高さはDOMでレンダリングされた後でしか決定できないため、Lifecycleメソッドにアクセスする必要があるため、Reactクラスコンポーネントを使用する必要があることを忘れないでくださいcomponentDidMount()

import React, {Component} from 'react'
import {render} from 'react-dom'

class DivSize extends Component {

  constructor(props) {
    super(props)

    this.state = {
      height: 0
    }
  }

  componentDidMount() {
    const height = this.divElement.clientHeight;
    this.setState({ height });
  }

  render() {
    return (
      <div 
        className="test"
        ref={ (divElement) => { this.divElement = divElement } }
      >
        Size: <b>{this.state.height}px</b> but it should be 18px after the render
      </div>
    )
  }
}

render(<DivSize />, document.querySelector('#container'))

ここで実行中の例を見つけることができます:https : //codepen.io/bassgang/pen/povzjKw


6
これは機能しますが、Airbnbスタイルガイドでいくつかのeslintエラーが発生します:<div ref={ divElement => this.divElement = divElement}>{children}</div>"[eslint] Arrow function should not return assignment。(no-return-assign)" and this.setState({ height });throws "[eslint] Do not use setState in componentDidMount(react / no- did-mount-set-state)」です。誰かがスタイルガイドに準拠したソリューションを手に入れましたか?
ティモ

7
それは関数(よりむしろ式)として振る舞うように、このように、括弧内の割り当てをラップref={ divElement => {this.divElement = divElement}}
teletypist

3
これは受け入れられる答えになるはずです:)また、要素が更新された後に要素のサイズを取得したい場合は、メソッドも使用できますcomponentDidUpdate
jjimenez

4
この例を使用して、componentDidMountでthis.setState()を呼び出す代替方法はありますか?
danjones_mcr

3
良い解決策。ただし、レスポンシブデザインでは機能しません。デスクトップのヘッダーの高さが50pxで、ユーザーがウィンドウサイズを縮小し、高さが100pxになった場合、このソリューションは更新された高さを取得しません。変更された効果を取得するには、ページを更新する必要があります。
TheCoder

96

の使用react hooksに興味がある人にとって、これはあなたが始めるのに役立つかもしれません。

import React, { useState, useEffect, useRef } from 'react'

export default () => {
  const [height, setHeight] = useState(0)
  const ref = useRef(null)

  useEffect(() => {
    setHeight(ref.current.clientHeight)
  })

  return (
    <div ref={ref}>
      {height}
    </div>
  )
}

11
あなたの答えをありがとう-それは私が始めるのに役立ちました。ただし、2つの質問:(1)これらのレイアウト測定タスクでは、useLayoutEffect代わりに使用する必要がありuseEffectますか?ドキュメントは私たちがすべきであることを示しているようです。こちらの「ヒント」セクションをご覧ください:reactjs.org/docs/hooks-effect.html#detailed-explanation (2)子要素への参照が必要なだけの場合は、useRef代わりにcreateRef?ドキュメントはuseRef、この使用例の方がより適切であることを示しているようです。参照: reactjs.org/docs/hooks-reference.html#useref
Jason Frank

私が提案している2つの項目を使用するソリューションを実装しました。彼らはうまく機能しているようですが、これらの選択のいくつかの欠点を知っているかもしれませんか?ご協力いただきありがとうございます!
Jason Frank

4
@JasonFrank良い質問です。1)レイアウトとペイントの後に、useEffectとuseLayoutEffectの両方が呼び出されます。ただし、useLayoutEffectは次のペイントの前に同期的に起動します。視覚的な一貫性が本当に必要な場合は、useLayoutEffectを使用します。それ以外の場合は、useEffectで十分です。2)そのとおりです!機能コンポーネントでは、createRefはコンポーネントがレンダリングされるたびに新しい参照を作成します。useRefを使用することをお勧めします。回答を更新します。ありがとう!

これは、フックで参照を使用する方法を理解するのに役立ちました。useLayoutEffectここで他のコメントを読んだ後、私はついに行きました。うまくいくようです。:)
GuiDoody

1
私は私のコンポーネントの寸法を設定していましたuseEffect(() => setDimensions({width: popup.current.clientWidth, height: popup.current.clientHeight}))、そして私のIDE(WebStorm)はこれを提案しました:ESLint:React Hook useEffectには 'setDimensions'への呼び出しが含まれています。依存関係のリストがないと、更新が無限に続く可能性があります。これを修正するには、useEffectフックの2番目の引数として[]を渡します。(react-hooks / exhaustive-deps)[]2番目の引数として渡すuseEffect
Akshdeep Singh

9

を使用する代わりに、要素にrefsを使用することもできますdocument.getElementById。これは、もう少し堅牢なものです。


@doublejoshは基本的にあなたの言うとおりですが、たとえばangularJsreact一方から他方に移行する際に混同する必要がある場合はどうしますか?直接DOM操作が本当に必要な場合があります
メッセルビル

2

私の2020(または2019)の回答

import React, {Component, useRef, useLayoutEffect} from 'react';
import { useDispatch } from 'react-redux';
import { Toast, ToastBody, ToastHeader } from 'reactstrap';

import {WidgetHead} from './WidgetHead';

export const Widget = ({title, toggle, reload, children, width, name}) => {
    let myself = useRef(null);
    const dispatch = useDispatch();
    useLayoutEffect(()=>{
        if (myself.current) {
            const height = myself.current.clientHeight
            dispatch({type:'GRID_WIDGET_HEIGHT', widget:name, height})
        }
    }, [myself.current, myself.current?myself.current.clientHeight:0])

    return (
        <Toast innerRef={myself}>
            <WidgetHead title={title}
                toggle={toggle}
                reload={reload} />
            <ToastBody>
            {children}
            </ToastBody>
        </Toast>
    )
}

ここで(WidgetHead)が欠落しているもののためにあなたの想像力を使ってみましょう、reactstrap交換してください:あなたはNPMに見つけることができるものであるinnerRefref、従来のDOM要素のために(言います<div>)。

useEffectまたはuseLayoutEffect

最後は変更に対して同期的であると言われています

useLayoutEffect(またはuseEffect)2番目の引数

2番目の引数は配列であり、最初の引数で関数を実行する前にチェックされます。

使った

[myself.current、myself.current?myself.current.clientHeight:0]

myself.currentはレンダリング前はnullであり、これは確認しない方が良いため、最後の2番目のパラメーターmyself.current.clientHeightは変更を確認するものです。

私がここで解決している(または解決しようとしている)

私はここで、自分の意志によって高さが変化するグリッド上のウィジェットの問題を解決しています。グリッドシステムは、反応するのに十分な弾力性を備えている必要があります(https://github.com/STRML/react-grid-layout)。


1
素晴らしい答えですが、より簡単な例から恩恵を受けるでしょう。基本的にはホワイトナイトの答えですがuseLayoutEffect、参照を結び付ける実際のdivがあります。
bot19

1

フックで使用する:

この回答は、ロード後にコンテンツディメンションが変更される場合に役立ちます。

onreadystatechange:要素またはHTMLドキュメントに属するデータのロード状態が変化すると発生します。onreadystatechangeイベントは、ページのコンテンツのロード状態が変更されたときにHTMLドキュメントで発生します。

import {useState, useEffect, useRef} from 'react';
const ref = useRef();
useEffect(() => {
    document.onreadystatechange = () => {
      console.log(ref.current.clientHeight);
    };
  }, []);

ロード後に寸法が変化する可能性のある埋め込みのYouTubeビデオプレーヤーで作業しようとしました。



0

ウィンドウのサイズ変更イベントが必要な場合は、次のようになります。

class DivSize extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
      width: 0,
      height: 0
    }
    this.resizeHandler = this.resizeHandler.bind(this);
  }

  resizeHandler() {
    const width = this.divElement.clientWidth;
    const height = this.divElement.clientHeight;
    this.setState({ width, height });
  }

  componentDidMount() {
    this.resizeHandler();
    window.addEventListener('resize', this.resizeHandler);
  }

  componentWillUnmount(){
    window.removeEventListener('resize', this.resizeHandler);
  }

  render() {
    return (
      <div 
        className="test"
        ref={ (divElement) => { this.divElement = divElement } }
      >
        Size: widht: <b>{this.state.width}px</b>, height: <b>{this.state.height}px</b>
      </div>
    )
  }
}

ReactDOM.render(<DivSize />, document.querySelector('#container'))

コードペン


0

別の解決策として、要素を視覚的にレンダリングする必要なくReact要素のサイズを同期的に取得したい場合は、ReactDOMServerDOMParserを使用できます。

FixedSizeListに必要な小道具をハードコードする必要はなく、react-windowreact-virtualized)を使用するときに、この関数を使用してリストアイテムレンダラーの高さを取得しますitemSize

Utilities.js:

/**
 * @description Common and reusable functions 
 * 
 * @requires react-dom/server
 * 
 * @public
 * @module
 * 
 */
import ReactDOMServer from "react-dom/server";

/**
 * @description Retrieve the width and/or heigh of a React element without rendering and committing the element to the DOM.
 * 
 * @param {object} elementJSX - The target React element written in JSX.
 * @return {object} 
 * @public
 * @function
 * 
 * @example
 * 
 * const { width, height } = getReactElementSize( <div style={{ width: "20px", height: "40px" }} ...props /> );
 * console.log(`W: ${width}, H: ${height});  // W: 20, H: 40
 * 
 */
const getReactElementSize = (elementJSX) => {

    const elementString = ReactDOMServer.renderToStaticMarkup(elementJSX);
    const elementDocument = new DOMParser().parseFromString(elementString, "text/html");
    const elementNode = elementDocument.getRootNode().body.firstChild;

    const container = document.createElement("div");
    const containerStyle = {

        display: "block",
        position: "absolute",
        boxSizing: "border-box",
        margin: "0",
        padding: "0",
        visibility: "hidden"
    };

    Object.assign(container.style, containerStyle);

    container.appendChild(elementNode);
    document.body.appendChild(container);

    const width = container.clientWidth;
    const height = container.clientHeight;

    container.removeChild(elementNode);
    document.body.removeChild(container);

    return {

        width,
        height
    };
};

/**
 * Export module
 * 
 */
export {

    getReactElementSize
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.