Uzyskaj rzutnię / wysokość okna w ReactJS

160

Jak uzyskać wysokość rzutni w ReactJS? W normalnym JavaScript używam

window.innerHeight()

ale używając ReactJS, nie jestem pewien, jak uzyskać te informacje. Rozumiem, że

ReactDOM.findDomNode()

działa tylko dla utworzonych komponentów. Nie dotyczy to jednak elementu documentor body, który mógłby mi podać wysokość okna.

Jabran Saeed
źródło

Odpowiedzi:

245

Ta odpowiedź jest podobna do odpowiedzi Jabrana Saeeda, z wyjątkiem tego, że obsługuje również zmianę rozmiaru okna. Mam to stąd .

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
źródło
3
Możesz usunąć .bind(this)z argumentów wywołania zwrotnego, ponieważ są one już powiązane przez konstruktor.
Scymex
1
Nitpick: kod w konstruktorze powinien być prawdopodobnie this.state = { width: 0, height: 0 };taki, aby zmienne stanu nie zmieniały swoich typów (jeśli dobrze rozumiem, window.innerWidth jest liczbą całkowitą ). Nie zmienia niczego poza tym, że ułatwia zrozumienie kodu IMHO. Dziękuję za odpowiedź!
johndodo
1
@johndodo. Ups. Edytowano.
speckledcarp
7
Dlaczego nie this.state = { width: window.innerWidth, height: window.innerHeight };zacząć?
Gerbus
1
Być może nie jest najlepszym pomysłem użycie wywołania zwrotnego w celu wskazania zdarzenia zmiany rozmiaru okna, a następnie ponowne wskazanie obiektu globalnego okna wewnątrz wywołania zwrotnego. Ze względu na wydajność, czytelność i konwencję zaktualizuję go, aby używał podanych wartości zdarzeń.
GoreDefex
177

Korzystanie z hooków (reagowanie 16.8.0+)

Utwórz useWindowDimensionshaczyk.

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

A potem będziesz mógł go używać w swoich komponentach, takich jak ten

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

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

Przykład roboczy

Oryginalna odpowiedź

Tak samo jest w Reakcie, którego możesz użyć, window.innerHeightaby uzyskać wysokość bieżącej rzutni.

Jak widać tutaj

QoP
źródło
2
window.innerHeight nie jest funkcją, to właściwość
Jairo,
2
wygląda na to, że Kevin Danikowski zredagował odpowiedź i jakoś ta zmiana została zatwierdzona. To jest teraz naprawione.
QoP
3
@FeCH usuwa detektor zdarzeń, gdy komponent jest odmontowany. Nazywa się to cleanupfunkcją, możesz o tym przeczytać tutaj
QoP
1
Jakieś pomysły na to samo podejście z SSR (NextJS)?
roadev
1
@roadev w NextJS, możesz również sprawdzić, czy reqrekwizyt jest dostępny getInitialProps. Jeśli tak, to działa na serwerze, nie będziesz mieć zmiennych okna.
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...
  }
}

Ustaw rekwizyty

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

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

wysokość widoku jest teraz dostępna jako {this.state.height} w szablonie renderowania

Jabran Saeed
źródło
13
To rozwiązanie nie aktualizuje się, jeśli rozmiar okna przeglądarki zostanie zmieniony
speckledcarp
1
FYI, aktualizacja stanu po zamontowaniu komponentu wyzwoli drugie wywołanie render () i może prowadzić do problemów z właściwościami / układami. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
Haukur Kristinsson
1
@HaukurKristinsson, jak to przezwyciężyć?
Richard,
1
@JabranSaeed, dlaczego nie po prostu ustawić wysokości w konstruktorze zamiast aktualizować go podczas montowania? Jeśli trzeba wziąć pod uwagę spostrzeżenia rekwizyty można domyślnie wartość na to tak: height: window.innerHeight || props.height. To nie tylko uprości kod, ale także usunie niepotrzebne zmiany stanu.
JohnnyQ,
componentWillMountnie jest już zalecany, patrz Reactjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd
26

Właśnie zredagowałem obecną odpowiedź QoP , aby obsługiwać SSR i używać jej z 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
źródło
Świetne rozwiązanie.
Jeremy E
Próbowałem to zrobić z NextJS, ale po zmianie rozmiaru ekranu wydaje się, że ma prawidłowy rozmiar. Zakładam, że dzieje się tak z powodu renderowania po stronie serwera nextJS. Czy masz jakies pomysły?
herohamp
15

Odpowiedź @speckledcarp jest świetna, ale może być uciążliwa, jeśli potrzebujesz tej logiki w wielu komponentach. Możesz zmienić to na HOC (komponent wyższego rzędu), aby ułatwić ponowne użycie tej logiki.

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

Następnie w głównym komponencie:

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

Możesz także układać w stosy HOC, jeśli masz inny, którego potrzebujesz, np withRouter(withWindowDimensions(MyComponent))

Edycja: W dzisiejszych czasach korzystałbym z haka React ( przykład powyżej ), ponieważ rozwiązują one niektóre zaawansowane problemy z HOCami i klasami

James L.
źródło
1
Dobra robota @James
Manish
8

Właśnie poświęciłem trochę czasu na zastanowienie się nad niektórymi rzeczami związanymi z Reagowaniem i przewijaniem wydarzeń / pozycji - więc dla tych, którzy wciąż szukają, oto co znalazłem:

Wysokość rzutni można znaleźć, używając window.innerHeight lub document.documentElement.clientHeight. (Bieżąca wysokość rzutni)

Wysokość całego dokumentu (treści) można znaleźć za pomocą window.document.body.offsetHeight.

Jeśli próbujesz znaleźć wysokość dokumentu i wiesz, kiedy osiągnąłeś dno - oto, co wymyśliłem:

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

(Mój pasek nawigacyjny miał 72 piksele w stałej pozycji, więc -72, aby uzyskać lepszy wyzwalacz zdarzenia przewijania)

Wreszcie, oto kilka poleceń przewijania do console.log (), które pomogły mi aktywnie zrozumieć matematykę.

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

Uff! Mam nadzieję, że to komuś pomoże!

thielr7
źródło
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
źródło
1
Witamy w Stack Overflow. Zrzuty kodu bez żadnego wyjaśnienia rzadko są pomocne. Stack Overflow polega na uczeniu się, a nie na udostępnianiu fragmentów do ślepego kopiowania i wklejania. Proszę edytować swoje pytanie i wyjaśnić, jak to działa lepiej niż to, co PO warunkiem.
Chris
2

Odpowiedzi @speckledcarp i @Jamesl są genialne. Jednak w moim przypadku potrzebowałem komponentu, którego wysokość mogłaby rozszerzyć pełną wysokość okna, warunkowo w czasie renderowania ... ale wywołanie HOC w ramach render()renderuje całe poddrzewo. BAAAD.

Poza tym nie byłem zainteresowany pobieraniem wartości jako rekwizytów, ale po prostu chciałem, aby rodzic divzajmował całą wysokość ekranu (lub szerokość lub obie).

Napisałem więc komponent Parent, który zawiera element div o pełnej wysokości (i / lub szerokości). Bum.

Przypadek użycia:

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

Oto składnik:

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
źródło
0

Możesz także spróbować tego:

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
źródło