Получить окно просмотра / высоту окна в ReactJS


161

Как получить высоту области просмотра в ReactJS? В обычном JavaScript я использую

window.innerHeight()

но используя ReactJS, я не уверен, как получить эту информацию. Я понимаю, что

ReactDOM.findDomNode()

работает только для созданных компонентов. Однако это не относится к элементу documentor body, который может дать мне высоту окна.

Ответы:


246

Этот ответ похож на ответ Джабрана Саида, за исключением того, что он также обрабатывает изменение размера окна. Я получил это отсюда .

constructor(props) {
  super(props);
  this.state = { width: 0, height: 0 };
  this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
}

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

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

updateWindowDimensions() {
  this.setState({ width: window.innerWidth, height: window.innerHeight });
}

3
Вы можете удалить .bind(this)из аргументов обратного вызова, так как он уже связан конструктором.
Scymex,

1
Nitpick: код в конструкторе, вероятно, должен быть this.state = { width: 0, height: 0 };таким, чтобы переменные состояния не меняли свои типы (если я правильно понимаю, window.innerWidth - целое число ). Ничего не меняет, кроме как облегчает понимание кода IMHO. Спасибо за ответ!
Джондо

1
@johndodo. Упс. Ред.
пятнышко

7
Почему бы не this.state = { width: window.innerWidth, height: window.innerHeight };начать?
Гербус

1
Возможно, это не самая лучшая идея - использовать обратный вызов для нацеливания события resize окна, а затем повторно нацелить глобальный объект окна внутри обратного вызова. Ради производительности, удобочитаемости и удобства я собираюсь обновить его, чтобы использовать заданные значения событий.
GoreDefex

178

Использование крючков (React 16.8.0+)

Создать useWindowDimensionsкрючок.

import { useState, useEffect } from 'react';

function getWindowDimensions() {
  const { innerWidth: width, innerHeight: height } = window;
  return {
    width,
    height
  };
}

export default function useWindowDimensions() {
  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());

  useEffect(() => {
    function handleResize() {
      setWindowDimensions(getWindowDimensions());
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return windowDimensions;
}

И после этого вы сможете использовать его в своих компонентах, как это

const Component = () => {
  const { height, width } = useWindowDimensions();

  return (
    <div>
      width: {width} ~ height: {height}
    </div>
  );
}

Рабочий пример

Оригинальный ответ

То же самое в React, вы можете использовать, window.innerHeightчтобы получить высоту текущего окна просмотра.

Как вы можете видеть здесь


2
window.innerHeight это не функция, это свойство
Jairo

2
Похоже, Кевин Даниковски отредактировал ответ и каким-то образом это изменение было одобрено. Это исправлено сейчас.
QoP

3
@FeCH удаляет прослушиватель событий, когда компонент отключен. Это называется cleanupфункцией, вы можете прочитать об этом здесь
QoP

1
Есть идеи, чтобы получить такой же подход с SSR (NextJS)?
roadev

1
@roadev на NextJS, вы также можете проверить req, доступна ли эта опора getInitialProps. Если это так, он работает на сервере, то у вас не будет переменных окна.
giovannipds

54
class AppComponent extends React.Component {

  constructor(props) {
    super(props);
    this.state = {height: props.height};
  }

  componentWillMount(){
    this.setState({height: window.innerHeight + 'px'});
  }

  render() {
    // render your component...
  }
}

Установить реквизит

AppComponent.propTypes = {
 height:React.PropTypes.string
};

AppComponent.defaultProps = {
 height:'500px'
};

Высота окна просмотра теперь доступна как {this.state.height} в шаблоне рендеринга.


13
Это решение не обновляется при изменении размера окна браузера
speckledcarp

1
К сведению, обновление состояния после монтирования компонента вызовет второй вызов render () и может привести к перебрасыванию свойств / макета. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/...
Хаукур Кристинссон

1
@HaukurKristinsson как это преодолеть?
Ричард

1
@JabranSaeed, почему бы просто не пойти дальше и установить высоту в конструкторе, а не обновлять его при монтировании? Если вам нужно принять во внимание реквизита вы можете по умолчанию значения для него , как это: height: window.innerHeight || props.height. Это не только упростит код, но и удалит ненужные изменения состояния.
JohnnyQ

componentWillMountбольше не рекомендуется, см. Reactionjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd

26

Я только что отредактировали QoP «s текущий ответ поддерживать SSR и использовать его с Next.js (React 16.8.0+):

/hooks/useWindowDimensions.js :

import { useState, useEffect } from 'react';

export default function useWindowDimensions() {

  const hasWindow = typeof window !== 'undefined';

  function getWindowDimensions() {
    const width = hasWindow ? window.innerWidth : null;
    const height = hasWindow ? window.innerHeight : null;
    return {
      width,
      height,
    };
  }

  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());

  useEffect(() => {
    if (hasWindow) {
      function handleResize() {
        setWindowDimensions(getWindowDimensions());
      }

      window.addEventListener('resize', handleResize);
      return () => window.removeEventListener('resize', handleResize);
    }
  }, [hasWindow]);

  return windowDimensions;
}

/yourComponent.js :

import useWindowDimensions from './hooks/useWindowDimensions';

const Component = () => {
  const { height, width } = useWindowDimensions();
  /* you can also use default values or alias to use only one prop: */
  // const { height: windowHeight = 480 } useWindowDimensions();

  return (
    <div>
      width: {width} ~ height: {height}
    </div>
  );
}

Отличное решение.
Джереми Е

Я пытался сделать это с NextJS, но, кажется, правильный размер только после изменения размера экрана. Я предполагаю, что это из-за рендеринга на стороне сервера nextJS. У тебя есть идеи?
Herohamp

15

Ответ @speckledcarp отличный, но он может быть утомительным, если вам нужна эта логика в нескольких компонентах. Вы можете изменить его как HOC (компонент более высокого порядка), чтобы облегчить повторное использование этой логики.

withWindowDimensions.jsx

import React, { Component } from "react";

export default function withWindowDimensions(WrappedComponent) {
    return class extends Component {
        state = { width: 0, height: 0 };

        componentDidMount() {
            this.updateWindowDimensions();
            window.addEventListener("resize", this.updateWindowDimensions);
        }

        componentWillUnmount() {
            window.removeEventListener("resize", this.updateWindowDimensions);
        }

        updateWindowDimensions = () => {
            this.setState({ width: window.innerWidth, height: window.innerHeight });
        };

        render() {
            return (
                <WrappedComponent
                    {...this.props}
                    windowWidth={this.state.width}
                    windowHeight={this.state.height}
                    isMobileSized={this.state.width < 700}
                />
            );
        }
    };
}

Тогда в вашем основном компоненте:

import withWindowDimensions from './withWindowDimensions.jsx';

class MyComponent extends Component {
  render(){
    if(this.props.isMobileSized) return <p>It's short</p>;
    else return <p>It's not short</p>;
}

export default withWindowDimensions(MyComponent);

Вы также можете «сложить» HOC, если у вас есть другой, который вам нужно использовать, например withRouter(withWindowDimensions(MyComponent))

Edit: я бы пошел с React ловушкой в ​​настоящее время ( пример выше здесь ), так как они решают некоторые сложные вопросы с HOCs и классами


1
Хорошая работа @James
Маниш Шарма

8

Я просто потратил некоторое серьезное время на выяснение некоторых вещей с помощью React и прокрутки событий / позиций - так что для тех, кто все еще смотрит, вот что я нашел:

Высота области просмотра может быть найдена с помощью window.innerHeight или с помощью document.documentElement.clientHeight. (Текущая высота окна просмотра)

Высоту всего документа (тела) можно узнать с помощью window.document.body.offsetHeight.

Если вы пытаетесь найти высоту документа и знаете, когда достигли дна - вот что я придумал:

if (window.pageYOffset >= this.myRefII.current.clientHeight && Math.round((document.documentElement.scrollTop + window.innerHeight)) < document.documentElement.scrollHeight - 72) {
        this.setState({
            trueOrNot: true
        });
      } else {
        this.setState({
            trueOrNot: false
        });
      }
    }

(Моя панель навигации была 72px в фиксированной позиции, поэтому -72, чтобы получить лучший триггер события прокрутки)

Наконец, вот несколько команд прокрутки для console.log (), которые помогли мне разобраться в моей математике.

console.log('window inner height: ', window.innerHeight);

console.log('document Element client hieght: ', document.documentElement.clientHeight);

console.log('document Element scroll hieght: ', document.documentElement.scrollHeight);

console.log('document Element offset height: ', document.documentElement.offsetHeight);

console.log('document element scrolltop: ', document.documentElement.scrollTop);

console.log('window page Y Offset: ', window.pageYOffset);

console.log('window document body offsetheight: ', window.document.body.offsetHeight);

Уф! Надеюсь, это поможет кому-то!


2

Ответы @speckledcarp и @Jamesl оба великолепны. В моем случае, однако, мне был нужен компонент, высота которого могла бы увеличить полную высоту окна, условно во время рендеринга ... но вызов HOC в пределах render()повторно рендерит все поддерево. BAAAD.

Кроме того, я не интересовался получением значений в качестве реквизита, а просто хотел родителя, divкоторый бы занимал всю высоту экрана (или ширину, или оба).

Итак, я написал родительский компонент, обеспечивающий полную высоту (и / или ширину) div. Boom.

Вариант использования:

class MyPage extends React.Component {
  render() {
    const { data, ...rest } = this.props

    return data ? (
      // My app uses templates which misbehave badly if you manually mess around with the container height, so leave the height alone here.
      <div>Yay! render a page with some data. </div>
    ) : (
      <FullArea vertical>
        // You're now in a full height div, so containers will vertically justify properly
        <GridContainer justify="center" alignItems="center" style={{ height: "inherit" }}>
          <GridItem xs={12} sm={6}>
            Page loading!
          </GridItem>
        </GridContainer>
      </FullArea>
    )

Вот этот компонент:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

class FullArea extends Component {
  constructor(props) {
    super(props)
    this.state = {
      width: 0,
      height: 0,
    }
    this.getStyles = this.getStyles.bind(this)
    this.updateWindowDimensions = this.updateWindowDimensions.bind(this)
  }

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

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

  getStyles(vertical, horizontal) {
    const styles = {}
    if (vertical) {
      styles.height = `${this.state.height}px`
    }
    if (horizontal) {
      styles.width = `${this.state.width}px`
    }
    return styles
  }

  updateWindowDimensions() {
    this.setState({ width: window.innerWidth, height: window.innerHeight })
  }

  render() {
    const { vertical, horizontal } = this.props
    return (
      <div style={this.getStyles(vertical, horizontal)} >
        {this.props.children}
      </div>
    )
  }
}

FullArea.defaultProps = {
  horizontal: false,
  vertical: false,
}

FullArea.propTypes = {
  horizontal: PropTypes.bool,
  vertical: PropTypes.bool,
}

export default FullArea

2
// just use (useEffect). every change will be logged with current value
import React, { useEffect } from "react";

export function () {
  useEffect(() => {
    window.addEventListener('resize', () => {
      const myWidth  = window.innerWidth;
      console.log('my width :::', myWidth)
   })
  },[window])

  return (
    <>
      enter code here
   </>
  )
}

1
Добро пожаловать в переполнение стека. Дампы кода без каких-либо объяснений редко бывают полезными. Переполнение стека касается обучения, а не предоставления фрагментов для слепого копирования и вставки. Пожалуйста, отредактируйте свой вопрос и объясните, как он работает лучше, чем предоставленный ОП.
Крис

Осторожно, этот код никогда не удаляет обработчик событий, который он создает. stackoverflow.com/a/36862446/867600 - лучший подход для ловли.
Кристоф Кадилхак

0

Вы также можете попробовать это:

constructor(props) {
        super(props);
        this.state = {height: props.height, width:props.width};
      }

componentWillMount(){
          console.log("WINDOW : ",window);
          this.setState({height: window.innerHeight + 'px',width:window.innerWidth+'px'});
      }

render() {
        console.log("VIEW : ",this.state);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.