在 ReactJS 中获取视口/窗口高度

Get viewport/window height in ReactJS

本文关键字:窗口 高度 视口 获取 ReactJS      更新时间:2023-09-26

如何在ReactJS中获取视口高度?在普通的JavaScript中,我使用

window.innerHeight()

但是使用 ReactJS,我不确定如何获取此信息。我的理解是

ReactDOM.findDomNode()

仅适用于创建的组件。但是,对于documentbody元素来说并非如此,这可能会给我窗口的高度。

使用 useSyncExternalStore() hook (React 18.0.0+

import { useSyncExternalStore } from 'react';
export function useWindowDimensions() {
    // the 3rd parameter is optional and only needed for server side rendering
    return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}
function subscribe(callback) {
    window.addEventListener('resize', callback);
    return () => window.removeEventListener('resize', callback);
}
function getSnapshot() {
    return { width: window.innerWidth, height: window.innerHeight };
}
function getServerSnapshot() {
    return {
        width: 0,
        height: 0,
    };
}

使用钩子(反应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来获取当前视口的高度。

正如你在这里看到的

这个答案与Jabran Saeed的类似,只是它也处理窗口大小调整。我从这里得到它。

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

我刚刚编辑了 QoP 的当前答案以支持 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>
  );
}
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} 的形式提供

我找到了 QoP 和 speckledcarp 的答案的简单组合,使用 React Hooks 和调整大小功能,代码行数略少:

const [width, setWidth]   = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
const updateDimensions = () => {
    setWidth(window.innerWidth);
    setHeight(window.innerHeight);
}
useEffect(() => {
    window.addEventListener("resize", updateDimensions);
    return () => window.removeEventListener("resize", updateDimensions);
}, []);

哦,是的,请确保resize事件用双引号引起来,而不是单引号。那个让我有点;)

@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,您也可以"堆叠"HOC,例如 withRouter(withWindowDimensions(MyComponent))

编辑:我现在会使用 React 钩子(上面的例子),因为它们解决了 HOC 和类的一些高级问题

使用钩子:

在这里使用 useLayoutEffect 更有效:

import { useState, useLayoutEffect } from 'react';
function getWindowDimensions() {
  const { innerWidth: width, innerHeight: height } = window;
  return {
    width,
    height
  };
}
export default function useWindowDimensions() {
  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
  useLayoutEffect(() => {
    function handleResize() {
      setWindowDimensions(getWindowDimensions());
    }
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return windowDimensions;
}

用法:

const { height, width } = useWindowDimensions();

带有一个小打字稿

import { useState, useEffect } from 'react';
interface WindowDimentions {
    width: number;
    height: number;
}
function getWindowDimensions(): WindowDimentions {
    const { innerWidth: width, innerHeight: height } = window;
    return {
      width,
      height
    };
  }
  
  export default function useWindowDimensions(): WindowDimentions {
    const [windowDimensions, setWindowDimensions] = useState<WindowDimentions>(
      getWindowDimensions()
    );
  
    useEffect(() => {
      function handleResize(): void {
        setWindowDimensions(getWindowDimensions());
      }
  
      window.addEventListener('resize', handleResize);
      return (): void => window.removeEventListener('resize', handleResize);
    }, []);
  
    return windowDimensions;
  }

添加此内容以实现多样性和干净的方法。

此代码使用函数样式方法。我使用了onresize而不是addEventListener,如其他答案中所述。

import { useState, useEffect } from "react";
export default function App() {
  const [size, setSize] = useState({
    x: window.innerWidth,
    y: window.innerHeight
  });
  const updateSize = () =>
    setSize({
      x: window.innerWidth,
      y: window.innerHeight
    });
  useEffect(() => (window.onresize = updateSize), []);
  return (
    <>
      <p>width is : {size.x}</p>
      <p>height is : {size.y}</p>
    </>
  );
}

我只是花了一些认真的时间弄清楚 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);

呼! 希望它对某人有所帮助!

美好的一天,

我知道我参加这个聚会迟到了,但让我告诉你我的答案。

const [windowSize, setWindowSize] = useState(null)
useEffect(() => {
    const handleResize = () => {
        setWindowSize(window.innerWidth)
    }
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
}, [])

欲了解更多详情,请访问 https://usehooks.com/useWindowSize/

你可以像这样创建自定义钩子: useWindowSize()

import { useEffect, useState } from "react";
import { debounce } from "lodash";
const getWindowDimensions = () => {
  const { innerWidth: width, innerHeight: height } = window;
  return { width, height };
};
export function useWindowSize(delay = 0) {
  const [windowDimensions, setWindowDimensions] = useState(
    getWindowDimensions()
  );
  useEffect(() => {
    function handleResize() {
      setWindowDimensions(getWindowDimensions());
    }
    const debouncedHandleResize = debounce(handleResize, delay);
    window.addEventListener("resize", debouncedHandleResize);
    return () => window.removeEventListener("resize", debouncedHandleResize);
  }, [delay]);
  return windowDimensions;
}
// 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
   </>
  )
}

@speckledcarp和@Jamesl的答案都很棒。但是,就我而言,我需要一个组件,其高度可以扩展整个窗口高度,这是渲染时的条件。但是在 render() 中调用 HOC 会重新呈现整个子树。巴阿德。

另外,我对将这些值作为道具不感兴趣,而只是想要一个占据整个屏幕高度(或宽度,或两者)的父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

我通过将getWindowDimensions函数包装在useCallback中来更新代码

,略有不同
import { useCallback, useLayoutEffect, useState } from 'react';
export default function useWindowDimensions() {
    const hasWindow = typeof window !== 'undefined';
    const getWindowDimensions = useCallback(() => {
        const windowWidth = hasWindow ? window.innerWidth : null;
        const windowHeight = hasWindow ? window.innerHeight : null;
        return {
            windowWidth,
            windowHeight,
        };
    }, [hasWindow]);
    const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
    useLayoutEffect(() => {
        if (hasWindow) {
            function handleResize() {
                setWindowDimensions(getWindowDimensions());
            }
            window.addEventListener('resize', handleResize);
            return () => window.removeEventListener('resize', handleResize);
        }
    }, [getWindowDimensions, hasWindow]);
    return windowDimensions;
}

在这里,您有投票最多的答案包装在一个节点包(经过测试的打字稿)中,可供使用。

安装:

npm i @teambit/toolbox.react.hooks.get-window-dimensions

用法:

import React from 'react';
import { useWindowDimensions } from '@teambit/toolbox.react.hooks.get-window-dimensions';
const MyComponent = () => {
  const { height, width } = useWindowDimensions();
  return (
    <>
      <h1>Window size</h1>
      <p>Height: {height}</p>
      <p>Width: {width}</p>
    </>
  );
};

我重新命令使用useSyncExternalStore

import { useSyncExternalStore } from "react";
const store = {
  size: {
    height: undefined,
    width: undefined
  }
};
export default function ChatIndicator() {
  const { height, width } = useSyncExternalStore(subscribe, getSnapshot);
  return (
    <h1>
      {width} {height}
    </h1>
  );
}
function getSnapshot() {
  if (
    store.size.height !== window.innerHeight ||
    store.size.width !== window.innerWidth
  ) {
    store.size = { height: window.innerHeight, width: window.innerWidth };
  }
  return store.size;
}
function subscribe(callback) {
  window.addEventListener("resize", callback);
  return () => {
    window.removeEventListener("resize", callback);
  };
}

如果你想试试吧:https://codesandbox.io/s/vibrant-antonelli-5cecpm

即使在调整窗口大小后,也能保持当前尺寸处于该状态的简单方法:

//set up defaults on page mount
componentDidMount() {
  this.state = { width: 0, height: 0 };
  this.getDimensions(); 
  //add dimensions listener for window resizing
  window.addEventListener('resize', this.getDimensions); 
}
//remove listener on page exit
componentWillUnmount() {
  window.removeEventListener('resize', this.getDimensions); 
}
//actually set the state to the window dimensions
getDimensions = () => {
  this.setState({ width: window.innerWidth, height: window.innerHeight });
  console.log(this.state);
}

这就是实现它并在 React 功能组件中实时获取窗口宽度和高度的方法:

import React, {useState, useEffect} from 'react' 
const Component = () => {
  const [windowWidth, setWindowWidth] = useState(0)
  const [windowHeight, setWindowHeight] = useState(0)
  
  useEffect(() => {
    window.addEventListener('resize', e => {
      setWindowWidth(window.innerWidth);
    });
  }, [window.innerWidth]);
  useEffect(() => {
    window.addEventListener('resize', e => {
      setWindowHeight(window.innerHeight);
    });
  }, [window.innerHeight]);
  return(
    <h3>Window width is: {windowWidth} and Height: {windowHeight}</h3>
  )
}

我使用了投票最多的答案,但是当每次返回不同的值时,useSyncExternalStore导致超出最大更新getSnapshot无限循环(即使属性宽度和高度相同,每次都会创建一个新对象),所以我更改为缓存最后一个值并在宽度和高度相同时使用它:

import { useSyncExternalStore } from 'react';
export function useWindowDimensions() {
    // the 3rd parameter is optional and only needed for server side rendering
    return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}
const subscribe = (
    callback: (this: Window, ev: WindowEventMap['resize']) => unknown,
) => {
    window.addEventListener('resize', callback);
    return () => window.removeEventListener('resize', callback);
};
export interface WindowSizes {
    width: number;
    height: number;
}
const getSnapshot = ((value: WindowSizes | undefined) => () => {
    if (
        !value ||
        value.width !== window.innerWidth ||
        value.height !== window.innerHeight
    ) {
        value = { width: window.innerWidth, height: window.innerHeight };
    }
    return value;
})(undefined);
const getServerSnapshot = (
    (
        value = {
            width: 0,
            height: 0,
        },
    ) =>
    () => {
        return value;
    }
)();

你也可以试试这个:

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

使用useEffect

很简单
useEffect(() => {
    window.addEventListener("resize", () => {
        updateDimention({ 
            ...dimension, 
            width: window.innerWidth, 
            height: window.innerHeight 
        });
        console.log(dimension);
    })
})
<</div> div class="answers">

作为答案来自: bren 但挂钩 useEffect 到 [window.innerWidth]

const [dimension, updateDimention] = useState();
  
  useEffect(() => {
    window.addEventListener("resize", () => {
        updateDimention({ 
            ...dimension, 
            width: window.innerWidth, 
            height: window.innerHeight 
        });
       
    })
},[window.innerWidth]);
 console.log(dimension);

React 原生 web 有一个可以使用的 useWindowDimensions 钩子:

    import { useWindowDimensions } from "react-native";
    const dimensions = useWindowDimensions()

有一个包含 93.000+ 下载的包,名为 useWindowSize()

npm i @react-hook/window-size

import {
  useWindowSize,
  useWindowWidth,
  useWindowHeight,
} from '@react-hook/window-size'
const Component = (props) => {
  const [width, height] = useWindowSize()
  const onlyWidth = useWindowWidth()
  const onlyHeight = useWindowHeight()
...
}

文档

@foad abdollahi 和 @giovannipds 答案的组合帮助我在 Nextjs 中使用带有 useLayoutEffect 的自定义钩子找到了解决方案。

function getWindowDimensions() {
    const { innerWidth: width, innerHeight: height } = window;
    return {
      width,
      height,
    };
  }
  function useWindowDimensions() {
    const [windowDimensions, setWindowDimensions] = useState(
      getWindowDimensions()
    );
    useLayoutEffect(() => {
      const isWindow = typeof window !== 'undefined';
      function handleResize() {
        setWindowDimensions(getWindowDimensions());
      }
      isWindow && window.addEventListener('resize', handleResize);
      console.log(windowDimensions);
      return () =>
        isWindow && window.removeEventListener('resize', handleResize);
    }, [windowDimensions]);
    return windowDimensions;
  }

与@QoP相同的答案,但在 Remix.run 中实现

import {useState, useEffect} from 'react';
function getWindowDimensions() {
  if (typeof window !== 'undefined') {
    const {innerWidth: width, innerHeight: height} = window;
    return {
      width,
      height,
    };
  } else {
    return null; // or return an empty object {}
  }
}
export default function useWindowDimensions() {
  const [windowDimensions, setWindowDimensions] = useState(
    getWindowDimensions() || {width: 0, height: 0},
  );
  useEffect(() => {
    function handleResize() {
      const dimensions = getWindowDimensions();
      if (dimensions) {
        setWindowDimensions(dimensions);
      }
    }
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return windowDimensions;
}

最终代码

import React, { useState, useEffect } from 'react'
export default function App() {
    const [screenSize, setScreenSize] = useState(getCurrentDimension());
    function getCurrentDimension(){
        return {
            width: window.innerWidth,
            height: window.innerHeight
        }
    }
  
    useEffect(() => {
            const updateDimension = () => {
                setScreenSize(getCurrentDimension())
            }
            window.addEventListener('resize', updateDimension);
    
        
            return(() => {
                window.removeEventListener('resize', updateDimension);
            })
    }, [screenSize])
    return (
        <div>
            <ul>
                <li>Width: <strong>{screenSize.width}</strong></li>
                <li>Height: <strong>{screenSize.height}</strong></li>
            </ul>    
        </div>
    )
}