ReactJS получает отображаемую высоту компонента

Я пытаюсь интегрировать или создать версию React https://github.com/kumailht/gridforms, для этого мне нужно нормализовать высоту столбцов внутри строки. Оригинал берет высоту строки сетки и применяет ее к столбцам детей.

Я планировал получить высоту строки, а затем сопоставить ее с свойством ребенка, хотя из моих попыток я думаю, что это может быть не идеальным или даже возможным?

Ниже мой текущий код.

GridRow = React.createClass({
  render(){
        const children = _.map(this.props.children, child => {
            child.props.height = // somehow get row component height
            return child
        })
    return (<div data-row-span={this.props.span} {...this.props}>
      {children}
    </div>)
  }
})

GridCol = React.createClass({
  render(){
    return (<div data-field-span={this.props.span} style={{height:this.props.height}} {...this.props}>
      {this.props.children}
    </div>)
  }
})

Я тестировал установку стиля таким образом, и он будет работать, но получить высоту нет.

EDIT: Fiddle:

https://jsfiddle.net/4wm5bffn/2/

Ответ 1

Немного поздно с ответом, но технически вы можете получить элемент hight таким образом:

var node = ReactDOM.findDOMNode(this.refs[ref-name]);
if (node){
  var calculatedHeight = node.clientHeight;
}

Ответ 2

В соответствии с текущими документами React предпочтительное использование refs - передать ему обратный вызов, а не строку, доступ к которой можно получить в другом месте this.refs. Таким образом, чтобы получить высоту div (в классе React.Component):

componentDidMount() {
  this.setState({ elementHeight: this.divRef.clientHeight });
}

render() {
  return <div ref={element => this.divRef = element}></div>
}

Или он работает таким образом, хотя я не знаю, является ли это целесообразным, поскольку мы устанавливаем состояние в методе рендеринга.

getHeight(element) {
  if (element && !this.state.elementHeight) { // need to check that we haven't already set the height or we'll create an infinite render loop
    this.setState({ elementHeight: element.clientHeight });
  }
}

render() {
  return <div ref={this.getHeight}></div>;
}

Ссылка: https://facebook.github.io/react/docs/more-about-refs.html

Ответ 3

Вот пример использования refs и clientWidth/clientHeight:

import React, { Component } from 'react';
import MyImageSrc from './../some-random-image.jpg'

class MyRandomImage extends Component {
  componentDidMount(){
    let { clientHeight, clientWidth } = this.refs.myImgContainer;
    console.log(clientHeight, clientWidth);
  }
  render() {
    return (
      <div ref="myImgContainer">
        <img src={MyImageSrc} alt="MyClickable" />
      </div>
    );
  }
}

export default MyRandomImage;

Примечание: это работает для width надежно, но не height. Будет редактировать, если я найду исправление...

Ответ 4

Не знаю ни о ком другом, но я всегда должен получить его на следующем тике, чтобы быть уверенным в правильной высоте и ширине. Чувствует себя бестолково, но я предполагаю, что это связано с циклом рендеринга, но сейчас я его возьму. onLayout может работать лучше в определенных случаях использования.

componentDidMount() {
  setTimeout(() => {
    let ref = this.refs.Container
    console.log(ref.clientHeight)
    console.log(ref.clientWidth)
  }, 1)
}

Ответ 5

Согласно этому ответу размеры компонента могут быть получены с нулевой шириной или высотой внутри обработчика события componentDidMount. Так что я вижу несколько способов решить это.

  1. Обработайте событие в компоненте React верхнего уровня и либо пересчитайте размеры там, либо перерисовайте конкретный дочерний компонент.

  2. Установите обработчик события загрузки для componentDidMount чтобы обрабатывать загрузку ячеек в компонент реагирования для пересчета нужных размеров:

    componentDidMount = () => {
       this.$carousel = $(this.carousel)
       window.addEventListener('load', this.componentLoaded)
    }
    

    Затем в методе componentLoaded просто сделайте то, что вам нужно.

Ответ 6

Мое личное мнение состоит в том, чтобы попытаться избежать использования статических и измеренных размеров, как это, если вы можете избежать этого, потому что это может осложнить приложение без необходимости. Но иногда вы не можете обойти это. Ваш компонент нужно будет установить, прежде чем вы сможете получить от него размер.

Общий подход:

  • Дайте элементу ссылку
  • Когда элемент визуализируется, возьмите ref и вызовите .clientHeight и/или .clientWidth
  • Поместите значения в состояние или пропустите с помощью реквизитов
  • Отметьте элемент, который нуждается в размере из переменных состояния

В вашем случае вы хотите захватить размер столбца, вы можете сделать что-то вроде:

GridRow = React.createClass({
  render(){
        const children = _.map(this.props.children, child => {
            child.props.height = // somehow get row component height
            return child
        })

    return (<div data-row-span={this.props.span} {...this.props}>
      <GridCol onSizeChange={(size) => {

         //Set it to state or whatever
         console.log("sizeOfCol", size);
      }} />
    </div>)
  }
})

GridCol = React.createClass({

  componentDidMount(){

    //Set stizes to the local state
    this.setState({
       colH: this.col.clientHeight,
       colW: this.col.clientWidth
    }); 

    //Use a callback on the props to give parent the data
    this.props.onSizeChange({colH: this.col.clientHeight, colW: this.col.clientWidth})

  }

  render(){
    //Here you save a ref (col) on the class
    return (<div ref={(col) => {this.col = col}} data-field-span={this.props.span} style={{height:this.props.height}} {...this.props}>
      <.... >
    </div>)
  }
})

Ответ 7

Я бы предпочел сделать это в componentDidUpdate, но, убедившись, что выполняется условие для предотвращения бесконечного цикла:

  componentDidUpdate(prevProps, prevState) {
    const articleContainer = document.getElementById('yourId');
    const width = articleContainer.clientWidth;
    
    if (this.state.width !== width) {
      this.setState({ width });
    }
  }