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
이것은 반응 질문이 아니라 자바 스크립트 및 DOM 질문입니다. 요소의 최종 높이를 찾기 위해 사용해야하는 DOM 이벤트를 파악해야합니다. 이벤트 핸들러에서를 사용 setState하여 상태 변수에 높이 값을 할당 할 수 있습니다 .
mostruash

답변:


28

참조 바이올린을 (실제로의 업데이트)

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 styleguide에 대한 몇 가지 eslint 오류가 발생합니다. <div ref={ divElement => this.divElement = divElement}>{children}</div>"[eslint] Arrow 함수가 할당을 반환하지 않아야합니다. (no-return-assign)" this.setState({ height });던지고 "[eslint] Do not use setState in componentDidMount (react / no- did-mount-set-state) ". 누구든지 스타일 가이드를 준수하는 솔루션이 있습니까?
Timo

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
답변 해 주셔서 감사합니다. 시작하는 데 도움이되었습니다. 하지만 두 가지 질문 : (1) 이러한 레이아웃 측정 작업의 경우 useLayoutEffect대신 useEffect? 문서는 우리가해야한다고 지시하는 것 같습니다. 여기에서 "팁"섹션을 참조하십시오. reactjs.org/docs/hooks-effect.html#detailed-explanation (2) 자식 요소에 대한 참조 만 필요한 이러한 경우에는 ? useRef대신 사용해야 합니다 createRef. 문서는 useRef이 사용 사례에 더 적합 하다는 것을 나타내는 것 같습니다 . 참조 : reactjs.org/docs/hooks-reference.html#useref
제이슨 프랭크

내가 제안하는 두 가지 항목을 사용하는 솔루션을 구현했습니다. 잘 작동하는 것 같지만 이러한 선택에 대한 몇 가지 단점을 알고 계십니까? 당신의 도움을 주셔서 감사합니다!
Jason Frank

4
@JasonFrank 좋은 질문입니다. 1) useEffect와 useLayoutEffect는 모두 레이아웃과 페인트 후에 실행됩니다. 그러나 useLayoutEffect는 다음 페인트 전에 동 기적으로 실행됩니다. 시각적 일관성이 정말로 필요한 경우 useLayoutEffect를 사용하고 그렇지 않으면 useEffect로 충분합니다. 2) 당신 말이 맞아요! 기능적 컴포넌트에서 createRef는 컴포넌트가 렌더링 될 때마다 새로운 ref를 생성합니다. useRef를 사용하는 것이 더 나은 옵션입니다. 내 대답을 업데이트하겠습니다. 감사!
Mr. 14

이것은 후크와 함께 ref를 사용하는 방법을 이해하는 데 도움이되었습니다. 나는 useLayoutEffect여기에서 다른 의견을 읽은 후에 함께 갔다 . 잘 작동하는 것 같습니다. :)
GuiDoody

1
내 구성 요소의 크기를 설정 useEffect(() => setDimensions({width: popup.current.clientWidth, height: popup.current.clientHeight}))하고 있었고 IDE (WebStorm)가 제안했습니다. ESLint : React Hook useEffect에는 'setDimensions'에 대한 호출이 포함되어 있습니다. 종속성 목록이 없으면 무한한 업데이트 체인으로 이어질 수 있습니다. 이 문제를 해결하려면 []를 useEffect Hook에 두 번째 인수로 전달하십시오. (react-hooks / exhaustive-deps) 이므로 []두 번째 인수로 전달 하십시오useEffect
Akshdeep Singh

9

을 사용하는 대신 요소에 refs를 사용하고 싶을 수도 있습니다 document.getElementById. 이것은 약간 더 강력한 것입니다.


@doublejosh 기본적으로 오른쪽하지만 당신은 예를 들어 호환 문제해야 할 경우 무엇을해야 angularJs하고 react, 다른 하나에서 마이그레이션하는 동안? 직접적인 DOM 조작이 정말로 필요한 경우가 있습니다
messerbill

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 요소에 대해 (A 말 <div>).

useEffect 또는 useLayoutEffect

마지막은 변경 사항에 대해 동 기적이라고합니다.

useLayoutEffect(또는 useEffect) 두 번째 인수

두 번째 인수는 배열이며 첫 번째 인수에서 함수를 실행하기 전에 확인됩니다.

나는 사용했다

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

myself.current는 렌더링 전에 null이고 확인하지 않는 것이 좋기 때문에 마지막에있는 두 번째 매개 변수는 myself.current.clientHeight변경 사항을 확인하려는 것입니다.

내가 여기서 해결하는 것 (또는 해결하려는 것)

나는 여기에서 자신의 의지로 높이를 변경하는 그리드의 위젯 문제를 해결하고 있으며 그리드 시스템은 반응 하기에 충분히 탄력적 이어야합니다 ( https://github.com/STRML/react-grid-layout ).


1
좋은 대답이지만 더 간단한 예에서 도움을 받았을 것입니다. 기본적으로 whiteknight의 대답이지만 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);
    };
  }, []);

로드 후 크기가 변경 될 수있는 유튜브 비디오 플레이어 임베딩으로 작업하려고했습니다.



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에 필요한 prop 을 하드 코딩하는 대신 react-window ( react-virtualized )를 사용할 때 목록 항목 렌더러의 높이를 가져옵니다 .itemSize

utility.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.