Ottieni l'altezza finestra / finestra in ReactJS


160

Come posso ottenere l'altezza del viewport in ReactJS? In JavaScript normale che uso

window.innerHeight()

ma usando ReactJS, non sono sicuro di come ottenere queste informazioni. La mia comprensione è quella

ReactDOM.findDomNode()

funziona solo per i componenti creati. Tuttavia, questo non è il caso dell'elemento documento body, che potrebbe darmi l'altezza della finestra.

Risposte:


246

Questa risposta è simile a quella di Jabran Saeed, tranne per il fatto che gestisce anche il ridimensionamento delle finestre. L'ho preso da qui .

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
È possibile rimuovere .bind(this)dagli argomenti di callback poiché è già associato dal costruttore.
Scymex,

1
Nitpick: il codice nel costruttore dovrebbe probabilmente essere in this.state = { width: 0, height: 0 };modo tale che gli stati vars non cambino i loro tipi (se capisco correttamente window.innerWidth è intero ). Non cambia nulla tranne che rende il codice più facile da capire IMHO. Grazie per la risposta!
johndodo,

1
@johndodo. Ops. Modificato.
speckledcarp

7
Perché non this.state = { width: window.innerWidth, height: window.innerHeight };iniziare?
Gerbus,

1
Forse non è la migliore idea, utilizzare un callback per indirizzare l'evento di ridimensionamento della finestra e quindi ri-targetizzare l'oggetto finestra globale all'interno del callback. Per motivi di prestazioni, leggibilità e convenzione, lo aggiornerò per utilizzare i valori di evento indicati.
GoreDefex,

177

Utilizzo dei ganci (Reagisci 16.8.0+)

Crea un useWindowDimensionsgancio.

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 dopo sarai in grado di usarlo nei tuoi componenti in questo modo

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

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

Esempio funzionante

Risposta originale

È lo stesso in React, è possibile utilizzare window.innerHeightper ottenere l'altezza della finestra corrente.

Come puoi vedere qui


2
window.innerHeight non è una funzione, è una proprietà
Jairo

2
sembra che Kevin Danikowski abbia modificato la risposta e in qualche modo quel cambiamento sia stato approvato. Ora è riparato.
QoP

3
@FeCH rimuove il listener di eventi quando il componente è smontato. Si chiama cleanupfunzione, puoi leggerlo qui
QoP

1
Qualche idea per ottenere lo stesso approccio con SSR (NextJS)?
Roadev

1
@roadev su NextJS, puoi anche verificare se l' reqelica è disponibile su getInitialProps. Se lo è, è in esecuzione sul server, quindi non avrai variabili della finestra.
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...
  }
}

Imposta gli oggetti di scena

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

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

l'altezza della finestra è ora disponibile come {this.state.height} nel modello di rendering


13
Questa soluzione non si aggiorna se la finestra del browser viene ridimensionata
speckledcarp

1
Cordiali saluti, l'aggiornamento dello stato dopo un montaggio del componente attiverà una seconda chiamata render () e può portare a un thrashing di proprietà / layout. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
Haukur Kristinsson

1
@HaukurKristinsson come superare questo?
Richard,

1
@JabranSaeed perché non andare avanti e impostare l'altezza sul costruttore invece di aggiornarlo sul supporto? Se avete bisogno di prendere in considerazione oggetti di scena è possibile di default il valore ad essa in questo modo: height: window.innerHeight || props.height. Ciò non solo semplifica il codice, ma rimuove anche le modifiche di stato non necessarie.
JohnnyQ,

componentWillMountnon è più consigliato, vedi reactjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd

26

Ho appena modificato la risposta attuale di QoP per supportare SSR e usarlo con 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>
  );
}

Ottima soluzione
Jeremy E

Ho provato a farlo con NextJS ma sembra che abbia la dimensione corretta solo dopo un ridimensionamento dello schermo. Suppongo sia a causa del rendering sul lato server di nextJS. Hai qualche idea?
Herohamp,

15

La risposta di @speckledcarp è ottima, ma può essere noiosa se hai bisogno di questa logica in più componenti. È possibile riformattare come HOC (componente di ordine superiore) per facilitare il riutilizzo di questa logica.

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

Quindi nel componente principale:

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

Puoi anche "impilare" HOC se ne hai un altro che devi usare, ad es withRouter(withWindowDimensions(MyComponent))

Modifica: Al giorno d'oggi andrei con un hook di React ( esempio qui sopra ), poiché risolvono alcuni dei problemi avanzati con HOC e classi


1
Bel lavoro @James
Manish sharma

8

Ho appena trascorso un po 'di tempo serio a capire alcune cose con React e scorrere eventi / posizioni - quindi per coloro che sono ancora alla ricerca, ecco cosa ho trovato:

L'altezza del viewport può essere trovata usando window.innerHeight o usando document.documentElement.clientHeight. (Altezza attuale della vista)

L'altezza dell'intero documento (corpo) può essere trovata usando window.document.body.offsetHeight.

Se stai cercando di trovare l'altezza del documento e sapere quando hai toccato il fondo, ecco cosa mi è venuto in mente:

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

(La mia barra di navigazione era 72px in posizione fissa, quindi il -72 per ottenere un trigger trigger di scorrimento migliore)

Infine, ecco alcuni comandi scroll su console.log (), che mi hanno aiutato a capire attivamente la mia matematica.

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

Meno male! Spero che aiuti qualcuno!


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

1
Benvenuto in Stack Overflow. I dump del codice senza alcuna spiegazione sono raramente utili. Stack Overflow riguarda l'apprendimento, non fornire frammenti da copiare e incollare alla cieca. Si prega di modificare la tua domanda e spiegare come funziona meglio di quello che l'OP fornito.
Chris,

2

Le risposte di @speckledcarp e @Jamesl sono entrambe geniali. Nel mio caso, tuttavia, avevo bisogno di un componente la cui altezza potesse estendere l'intera altezza della finestra, a condizione che al momento del rendering ... ma la chiamata di un HOC all'interno render()riattivi l'intero sottotree. Baaad.

Inoltre, non ero interessato a ottenere i valori come oggetti di scena, ma volevo semplicemente un genitore divche occupasse l'intera altezza dello schermo (o larghezza o entrambi).

Quindi ho scritto un componente Parent che fornisce un div a tutta altezza (e / o larghezza). Boom.

Un caso d'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>
    )

Ecco il 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

0

Puoi anche provare questo:

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.