Obter altura da janela de visualização / janela no ReactJS

160

Como obtenho a altura da janela de exibição no ReactJS? Em JavaScript normal eu uso

window.innerHeight()

mas usando o ReactJS, não sei como obter essas informações. Meu entendimento é que

ReactDOM.findDomNode()

funciona apenas para componentes criados. No entanto, esse não é o caso do elemento documentou body, o que pode me dar uma altura da janela.

Jabran Saeed
fonte

Respostas:

245

Esta resposta é semelhante à de Jabran Saeed, exceto que também lida com o redimensionamento de janelas. Eu peguei daqui .

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 });
}
speckledcarp
fonte
3
Você pode remover .bind(this)dos argumentos de retorno de chamada, já que ele já está vinculado pelo construtor.
Scymex
1
Nitpick: o código no construtor provavelmente deve ser this.state = { width: 0, height: 0 };para que os vars de estado não alterem seus tipos (se eu entendi corretamente window.innerWidth é inteiro ). Não muda nada, exceto torna o código mais fácil de entender IMHO. Obrigado pela resposta!
Johndodo
1
@johndodo. Ops. Editado.
Speckledcarp
7
Por que não this.state = { width: window.innerWidth, height: window.innerHeight };começar?
Gerbus 28/01
1
Talvez não seja a melhor idéia, use um retorno de chamada para direcionar o evento de redimensionamento da janela e, em seguida, redirecionar novamente o objeto de janela global dentro do retorno de chamada. Por questões de desempenho, legibilidade e convenções, vou atualizá-lo para usar os valores de evento fornecidos.
GoreDefex
176

Usando ganchos (reagir 16.8.0+)

Crie um useWindowDimensionsgancho.

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;
}

E depois disso, você poderá usá-lo em seus componentes como este

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

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

Exemplo de trabalho

Resposta original

É o mesmo em React, você pode usar window.innerHeightpara obter a altura da viewport atual.

Como você pode ver aqui

QoP
fonte
2
window.innerHeight não é uma função, é uma propriedade #
Jairo
2
parece que Kevin Danikowski editou a resposta e de alguma forma essa alteração foi aprovada. Está consertado agora.
QoP
3
@FeCH remove o ouvinte de evento quando o componente é desmontado. É chamado de cleanupfunção, você pode ler sobre ele aqui
QoP
1
Alguma idéia para obter a mesma abordagem com o SSR (NextJS)?
roadev 5/09/19
1
@roadev no NextJS, você também pode verificar se o reqsuporte está disponível em getInitialProps. Se estiver, está sendo executado no servidor, então você não terá variáveis ​​de janela.
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...
  }
}

Defina os adereços

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

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

a altura da viewport agora está disponível como {this.state.height} no modelo de renderização

Jabran Saeed
fonte
13
Esta solução não atualizar se a janela do navegador é redimensionada
speckledcarp
1
Para sua informação, atualizar o estado após a montagem de um componente acionará uma segunda chamada de renderização () e pode levar à trituração de propriedade / layout. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
Haukur Kristinsson
1
@HaukurKristinsson como superar isso?
Richard
1
@JabranSaeed, por que não seguir em frente e definir a altura do construtor em vez de atualizá-lo no mount? Se você precisa tomar adereços em consideração que você pode usar como padrão o valor a ele assim: height: window.innerHeight || props.height. Isso não apenas simplifica o código, mas também remove alterações de estado desnecessárias.
JohnnyQ
componentWillMountnão é mais recomendado, consulte reactjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd
25

Acabei de editar a resposta atual do QoP para dar suporte ao SSR e usá-lo com o 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>
  );
}
giovannipds
fonte
Ótima solução.
Jeremy E
Tentei fazer isso com o NextJS, mas ele parece ter apenas o tamanho correto após o redimensionamento da tela. Suponho que seja por causa da renderização do lado do servidor nextJS. Você tem alguma ideia?
herohamp
15

A resposta de @speckledcarp é ótima, mas pode ser entediante se você precisar dessa lógica em vários componentes. Você pode refatorá-lo como um HOC (componente de ordem superior) para facilitar a reutilização dessa lógica.

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}
                />
            );
        }
    };
}

Então no seu componente principal:

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);

Você também pode "empilhar" HOCs se tiver outro que precise usar, por exemplo withRouter(withWindowDimensions(MyComponent))

Edit: Eu usaria um gancho React hoje em dia ( exemplo acima aqui ), pois eles resolvem alguns dos problemas avançados com HOCs e classes

James L.
fonte
1
Bom trabalho @James
Manish sharma
8

Passei um tempo sério tentando descobrir algumas coisas com React e rolando eventos / posições - então, para aqueles que ainda procuram, eis o que eu descobri:

A altura da janela de visualização pode ser encontrada usando window.innerHeight ou document.documentElement.clientHeight. (Altura atual da janela de visualização)

A altura do documento (corpo) inteiro pode ser encontrada usando window.document.body.offsetHeight.

Se você está tentando encontrar a altura do documento e sabe quando chegou ao fundo, eis o que eu vim:

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
        });
      }
    }

(Minha barra de navegação tinha 72px na posição fixa, portanto -72 para obter um melhor acionador de evento de rolagem)

Por fim, aqui estão alguns comandos de rolagem para console.log (), que me ajudaram a descobrir minha matemática ativamente.

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);

Uau! Espero que ajude alguém!

thielr7
fonte
3
// 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
   </>
  )
}
Joabe
fonte
1
Bem-vindo ao Stack Overflow. Despejos de código sem qualquer explicação raramente são úteis. O estouro de pilha é sobre aprendizado, não fornecendo trechos para copiar e colar cegamente. Por favor edite sua pergunta e explicar como ele funciona melhor do que o que o OP fornecido.
Chris
2

As respostas de @speckledcarp e @Jamesl são brilhantes. No meu caso, no entanto, eu precisava de um componente cuja altura pudesse estender a altura total da janela, condicional no momento da renderização ... mas chamando um HOC dentrorender() novamente renderiza toda a subárvore. BAAAD.

Além disso, eu não estava interessado em obter os valores como adereços, mas simplesmente queria um pai divque ocupasse toda a altura da tela (ou largura, ou ambos).

Então, eu escrevi um componente Parent fornecendo uma div total de altura (e / ou largura). Estrondo.

Um caso de uso:

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>
    )

Aqui está o componente:

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
thclark
fonte
0

Você também pode tentar o seguinte:

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);
}
Shubham Verma
fonte