Dapatkan viewport / tinggi jendela di ReactJS

160

Bagaimana cara mendapatkan ketinggian viewport di ReactJS? Dalam JavaScript normal saya gunakan

window.innerHeight()

tetapi menggunakan ReactJS, saya tidak yakin bagaimana mendapatkan informasi ini. Pemahaman saya adalah itu

ReactDOM.findDomNode()

hanya berfungsi untuk komponen yang dibuat. Namun ini bukan kasus untuk elemen documentatau body, yang bisa memberi saya ketinggian jendela.

Jabran Saeed
sumber

Jawaban:

245

Jawaban ini mirip dengan Jabran Saeed, kecuali itu juga menangani pengubahan ukuran jendela. Saya mendapatkannya dari sini .

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
sumber
3
Anda dapat menghapus .bind(this)dari argumen callback karena sudah terikat oleh konstruktor.
Scymex
1
Nitpick: kode dalam constructor mungkin harus this.state = { width: 0, height: 0 };agar vars state tidak mengubah tipenya (jika saya mengerti benar window.innerWidth adalah integer ). Tidak mengubah apa pun kecuali membuat kode lebih mudah untuk memahami IMHO. Terima kasih atas jawabannya!
johndodo
1
@johndodo. Aduh. Diedit.
speckledcarp
7
Mengapa tidak this.state = { width: window.innerWidth, height: window.innerHeight };memulai?
Gerbus
1
Mungkin itu bukan ide terbaik untuk, menggunakan panggilan balik untuk menargetkan acara mengubah ukuran jendela dan kemudian menargetkan kembali objek jendela global di dalam panggilan balik. Demi kinerja, keterbacaan, dan konvensi, saya akan memperbaruinya untuk menggunakan nilai acara yang diberikan.
GoreDefex
176

Menggunakan Hooks (Bereaksi 16.8.0+)

Buat useWindowDimensionskail.

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

Dan setelah itu Anda akan dapat menggunakannya dalam komponen Anda seperti ini

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

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

Contoh kerja

Jawaban asli

Ini sama dalam Bereaksi, Anda dapat menggunakan window.innerHeightuntuk mendapatkan ketinggian viewport saat ini.

Seperti yang Anda lihat di sini

QoP
sumber
2
window.innerHeight bukan fungsi, ini properti
Jairo
2
Sepertinya Kevin Danikowski mengedit jawaban dan entah bagaimana perubahan itu disetujui. Sudah diperbaiki sekarang.
QoP
3
@FeCH menghapus pendengar acara ketika komponen dilepas. Ini disebut cleanupfungsi, Anda dapat membacanya di sini
QoP
1
Adakah ide untuk mendapatkan pendekatan yang sama dengan SSR (NextJS)?
roadev
1
@roadev di NextJS, Anda juga dapat memeriksa apakah reqprop tersedia getInitialProps. Jika ya, itu berjalan di server, maka Anda tidak akan memiliki variabel jendela.
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...
  }
}

Atur alat peraga

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

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

tinggi viewport sekarang tersedia sebagai {this.state.height} di rendering template

Jabran Saeed
sumber
13
Solusi ini tidak diperbarui jika jendela browser diubah ukurannya
speckledcarp
1
FYI, memperbarui keadaan setelah komponen mount akan memicu panggilan render () kedua dan dapat menyebabkan properti / tata letak meronta-ronta. github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/…
Haukur Kristinsson
1
@HaukurKristinsson bagaimana cara mengatasinya?
Richard
1
@ Jabraneed mengapa tidak hanya pergi ke depan dan mengatur ketinggian pada konstruktor daripada memperbarui di gunung? Jika Anda perlu untuk mengambil alat peraga mempertimbangkan Anda bisa default nilai itu seperti ini: height: window.innerHeight || props.height. Ini tidak hanya akan menyederhanakan kode tetapi juga menghapus perubahan status yang tidak perlu.
JohnnyQ
componentWillMounttidak lagi direkomendasikan, lihat reactjs.org/docs/react-component.html#unsafe_componentwillmount
holmberd
26

Aku baru saja diedit QOP 's jawaban saat untuk mendukung RSK dan menggunakannya dengan Next.js (Bereaksi 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;
}

/yComponent.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
sumber
Solusi bagus
Jeremy E
Saya mencoba melakukan ini dengan NextJS tetapi tampaknya hanya memiliki ukuran yang benar setelah ukuran layar. Saya menganggap itu karena rendering sisi server nextJS. Apakah Anda punya ide?
herohamp
15

Jawaban @speckledcarp sangat bagus, tetapi bisa membosankan jika Anda membutuhkan logika ini di beberapa komponen. Anda bisa mengubahnya sebagai HOC (komponen urutan lebih tinggi) untuk membuat logika ini lebih mudah digunakan kembali.

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

Kemudian di komponen utama Anda:

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

Anda juga dapat "menumpuk" HOC jika Anda memiliki HOC lain yang perlu Anda gunakan, mis withRouter(withWindowDimensions(MyComponent))

Sunting: Saya akan menggunakan React hook saat ini ( contoh di atas sini ), karena mereka memecahkan beberapa masalah tingkat lanjut dengan HOC dan kelas

James L.
sumber
1
Kerja bagus @ames
Manish sharma
8

Saya hanya menghabiskan waktu yang serius untuk mencari tahu beberapa hal dengan Bereaksi dan menggulir acara / posisi - jadi bagi yang masih mencari, inilah yang saya temukan:

Ketinggian viewport dapat ditemukan dengan menggunakan window.innerHeight atau dengan menggunakan document.documentElement.clientHeight. (Tinggi viewport saat ini)

Ketinggian seluruh dokumen (tubuh) dapat ditemukan menggunakan window.document.body.offsetHeight.

Jika Anda berusaha menemukan ketinggian dokumen dan tahu kapan Anda telah mencapai dasarnya - inilah yang saya kemukakan:

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

(Navav saya adalah 72px dalam posisi tetap, sehingga -72 untuk mendapatkan pemicu scroll-event yang lebih baik)

Terakhir, berikut adalah sejumlah perintah gulir ke console.log (), yang membantu saya mengetahui matematika saya secara aktif.

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

Wah! Semoga ini bisa membantu seseorang!

thielr7
sumber
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
sumber
1
Selamat datang di Stack Overflow. Kesedihan kode tanpa penjelasan apa pun jarang membantu. Stack Overflow adalah tentang belajar, bukan menyediakan cuplikan untuk menyalin dan menempel secara membabi buta. Harap edit pertanyaan Anda dan jelaskan cara kerjanya lebih baik daripada yang diberikan OP.
Chris
2

Jawaban oleh @speckledcarp dan @Jamesl keduanya brilian. Namun, dalam kasus saya, saya membutuhkan komponen yang tingginya dapat memperpanjang ketinggian jendela penuh, bersyarat pada waktu render .... tetapi memanggil HOC dalam render()membuat ulang seluruh subtree. BAAAD.

Plus, saya tidak tertarik untuk mendapatkan nilai sebagai alat peraga tetapi hanya ingin orangtua divyang akan menempati seluruh ketinggian layar (atau lebar, atau keduanya).

Jadi saya menulis komponen Induk memberikan div tinggi penuh (dan / atau lebar). Ledakan.

Kasus penggunaan:

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

Inilah komponennya:

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
sumber
0

Anda juga dapat mencoba ini:

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
sumber