使用 React 在浏览器调整大小时重新渲染视图

Rerender view on browser resize with React

本文关键字:小时 新渲染 视图 调整 React 浏览器 使用      更新时间:2023-09-26

如何让 React 在调整浏览器窗口大小时重新渲染视图?

背景

我有一些块,我想在页面上单独布局,但是我也希望在浏览器窗口更改时更新它们。最终的结果将是类似于Ben Holland的Pinterest布局,但使用React编写的不仅仅是jQuery。我还有很长的路要走。

法典

这是我的应用程序:

var MyApp = React.createClass({
  //does the http get from the server
  loadBlocksFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      mimeType: 'textPlain',
      success: function(data) {
        this.setState({data: data.events});
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentWillMount: function() {
    this.loadBlocksFromServer();
  },    
  render: function() {
    return (
        <div>
      <Blocks data={this.state.data}/>
      </div>
    );
  }
});
React.renderComponent(
  <MyApp url="url_here"/>,
  document.getElementById('view')
)

然后我有Block组件(相当于上面Pinterest示例中的Pin(:

var Block = React.createClass({
  render: function() {
    return (
        <div class="dp-block" style={{left: this.props.top, top: this.props.left}}>
        <h2>{this.props.title}</h2>
        <p>{this.props.children}</p>
        </div>
    );
  }
});

Blocks的列表/集合:

var Blocks = React.createClass({
  render: function() {
    //I've temporarily got code that assigns a random position
    //See inside the function below...
    var blockNodes = this.props.data.map(function (block) {   
      //temporary random position
      var topOffset = Math.random() * $(window).width() + 'px'; 
      var leftOffset = Math.random() * $(window).height() + 'px'; 
      return <Block order={block.id} title={block.summary} left={leftOffset} top={topOffset}>{block.description}</Block>;
    });
    return (
        <div>{blockNodes}</div>
    );
  }
});

问题

我应该添加jQuery的窗口大小调整吗?如果是,在哪里?

$( window ).resize(function() {
  // re-render the component
});

有没有更"反应"的方式来做到这一点?

使用 React Hooks:

您可以定义一个自定义 Hook 来侦听窗口resize事件,如下所示:

import React, { useLayoutEffect, useState } from 'react';
function useWindowSize() {
  const [size, setSize] = useState([0, 0]);
  useLayoutEffect(() => {
    function updateSize() {
      setSize([window.innerWidth, window.innerHeight]);
    }
    window.addEventListener('resize', updateSize);
    updateSize();
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  return size;
}
function ShowWindowDimensions(props) {
  const [width, height] = useWindowSize();
  return <span>Window size: {width} x {height}</span>;
}

这里的优点是逻辑被封装,你可以在任何你想使用窗口大小的地方使用这个 Hook。

使用 React 类:

你可以在componentDidMount中收听,就像这个组件一样,它只显示窗口尺寸(如<span>Window size: 1024 x 768</span>(:

import React from 'react';
class ShowWindowDimensions extends React.Component {
  state = { width: 0, height: 0 };
  render() {
    return <span>Window size: {this.state.width} x {this.state.height}</span>;
  }
  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  };
  componentDidMount() {
    window.addEventListener('resize', this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.updateDimensions);
  }
}

@SophieAlpert是对的,+1,我只想根据这个答案提供她的解决方案的修改版本,没有 jQuery

var WindowDimensions = React.createClass({
    render: function() {
        return <span>{this.state.width} x {this.state.height}</span>;
    },
    updateDimensions: function() {
    var w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0],
        width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight;
        this.setState({width: width, height: height});
        // if you are using ES2015 I'm pretty sure you can do this: this.setState({width, height});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    }
});

一个非常简单的解决方案:

resize = () => this.forceUpdate()
componentDidMount() {
  window.addEventListener('resize', this.resize)
}
componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}

这是一个在没有jQuery的情况下使用es6的简单而简短的例子。

import React, { Component } from 'react';
export default class CreateContact extends Component {
  state = {
    windowHeight: undefined,
    windowWidth: undefined
  }
  handleResize = () => this.setState({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  });
  componentDidMount() {
    this.handleResize();
    window.addEventListener('resize', this.handleResize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize)
  }
  render() {
    return (
      <span>
        {this.state.windowWidth} x {this.state.windowHeight}
      </span>
    );
  }
}

import React, { useEffect, useState } from "react";
let App = () => {
  const [windowWidth, setWindowWidth] = useState(0);
  const [windowHeight, setWindowHeight] = useState(0);
  let resizeWindow = () => {
    setWindowWidth(window.innerWidth);
    setWindowHeight(window.innerHeight);
  };
  useEffect(() => {
    resizeWindow();
    window.addEventListener("resize", resizeWindow);
    return () => window.removeEventListener("resize", resizeWindow);
  }, []);
  return (
    <div>
      <span>
        {windowWidth} x {windowHeight}
      </span>
    </div>
  );
};

2020 年更新。 对于认真关心性能的 React 开发人员。

上述解决方案确实有效,但每当窗口大小更改单个像素时,都会重新渲染您的组件。

这通常会导致性能问题,所以我编写了useWindowDimension钩子,在短时间内对resize事件进行反反弹。 例如 100ms

import React, { useState, useEffect } from 'react';
export function useWindowDimension() {
  const [dimension, setDimension] = useState([
    window.innerWidth,
    window.innerHeight,
  ]);
  useEffect(() => {
    const debouncedResizeHandler = debounce(() => {
      console.log('***** debounced resize'); // See the cool difference in console
      setDimension([window.innerWidth, window.innerHeight]);
    }, 100); // 100ms
    window.addEventListener('resize', debouncedResizeHandler);
    return () => window.removeEventListener('resize', debouncedResizeHandler);
  }, []); // Note this empty array. this effect should run only on mount and unmount
  return dimension;
}
function debounce(fn, ms) {
  let timer;
  return _ => {
    clearTimeout(timer);
    timer = setTimeout(_ => {
      timer = null;
      fn.apply(this, arguments);
    }, ms);
  };
}

像这样使用它。

function YourComponent() {
  const [width, height] = useWindowDimension();
  return <>Window width: {width}, Window height: {height}</>;
}

从 React 16.8 开始,您可以使用 Hooks!

/* globals window */
import React, { useState, useEffect } from 'react'
import _debounce from 'lodash.debounce'
const Example = () => {
  const [width, setWidth] = useState(window.innerWidth)
  useEffect(() => {
    const handleResize = _debounce(() => setWidth(window.innerWidth), 100)
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    }
  }, [])
  return <>Width: {width}</>
}

编辑 2018:现在 React 对上下文有一流的支持


我将尝试给出一个通用的答案,针对这个特定问题,但也是一个更普遍的问题。

如果你不关心副作用库,你可以简单地使用类似Packery的东西。

如果使用 Flux,则可以创建一个包含窗口属性的存储,以便保留纯呈现函数,而不必每次都查询窗口对象。

在其他情况下,如果您想构建响应式网站,但您更喜欢 React 内联样式而不是媒体查询,或者希望 HTML/JS 行为根据窗口宽度而变化,请继续阅读:

什么是 React 上下文以及我为什么要谈论它

React 上下文不在公共 API 中,允许将属性传递给整个组件层次结构。

React 上下文对于将永不改变的东西传递给整个应用程序特别有用(许多 Flux 框架通过 mixin 使用它(。您可以使用它来存储应用业务不变量(如连接的 userId,以便它可在任何位置使用(。

但它也可以用来存储可以改变的东西。问题是,当上下文更改时,应重新呈现使用它的所有组件,并且这样做并不容易,最好的解决方案通常是使用新上下文卸载/重新装载整个应用程序。请记住,forceUpdate 不是递归的。

因此,

正如您所知,上下文是实用的,但是当它发生变化时会影响性能,因此它不应该经常更改。

在上下文中放置什么

  • 不变量:比如连接的用户ID,会话令牌,等等...
  • 不经常改变的事情

以下是不经常更改的内容:

当前用户语言

不会经常更改,当它发生时,随着整个应用程序的翻译,我们必须重新渲染所有内容:热语言更改的一个非常好的用例

窗口属性

宽度和高度不会经常变化,但当我们做我们的布局和行为时可能必须适应。对于布局,有时使用 CSS 媒体查询很容易自定义,但有时则不然,需要不同的 HTML 结构。对于行为,您必须使用Javascript处理此问题。

您不希望在每个调整大小事件上重新呈现所有内容,因此必须对调整大小事件进行反抖动。

我对你的问题的了解是,你想知道根据屏幕宽度显示多少个项目。因此,您首先必须定义响应断点,并枚举可以拥有的不同布局类型的数量。

例如:

  • 布局"1col",宽度 <= 600
  • 布局"2col",宽度为 600 <<1000
  • 布局"3col",表示 1000 <= 宽度

在调整大小事件(去抖动(时,可以通过查询窗口对象轻松获取当前布局类型。

然后,您可以将布局类型

与以前的布局类型进行比较,如果它已更改,请使用新上下文重新呈现应用:这允许在用户触发调整大小事件但实际上布局类型未更改时完全避免重新呈现应用,因此您仅在需要时重新呈现。

一旦有了它,您就可以简单地使用应用程序内的布局类型(可通过上下文访问(,以便您可以自定义 HTML、行为、CSS 类......您在 React 渲染函数中知道您的布局类型,因此这意味着您可以使用内联样式安全地编写响应式网站,并且根本不需要媒体查询。

如果你使用 Flux,你可以使用商店而不是 React 上下文,但是如果你的应用有很多响应式组件,也许使用上下文更简单?

我使用@senornestor的解决方案,但为了完全正确,您还必须删除事件侦听器:

componentDidMount() {
    window.addEventListener('resize', this.handleResize);
}
componentWillUnmount(){
    window.removeEventListener('resize', this.handleResize);
}
handleResize = () => {
    this.forceUpdate();
};

否则,您将收到警告:

警告:forceUpdate(...(:只能更新已装载或已装载 元件。这通常意味着你在未挂载的 forceUpdate(( 上调用了 forceUpdate(( 元件。这是无操作。请检查XXX的代码 元件。

我会跳过上述所有答案,开始使用react-dimensions高阶组件。

https://github.com/digidem/react-dimensions

只需添加一个简单的import和函数调用,您就可以访问组件中的this.props.containerWidththis.props.containerHeight

// Example using ES6 syntax
import React from 'react'
import Dimensions from 'react-dimensions'
class MyComponent extends React.Component {
  render() (
    <div
      containerWidth={this.props.containerWidth}
      containerHeight={this.props.containerHeight}
    >
    </div>
  )
}
export default Dimensions()(MyComponent) // Enhanced component

想分享我刚刚使用 window.matchMedia 发现的这个非常酷的东西

const mq = window.matchMedia('(max-width: 768px)');
  useEffect(() => {
    // initial check to toggle something on or off
    toggle();
    // returns true when window is <= 768px
    mq.addListener(toggle);
    // unmount cleanup handler
    return () => mq.removeListener(toggle);
  }, []);
  // toggle something based on matchMedia event
  const toggle = () => {
    if (mq.matches) {
      // do something here
    } else {
      // do something here
    }
  };

如果窗口高于或低于指定的最大宽度值,.matches将返回 true 或 false,这意味着无需限制侦听器,因为 matchMedia 仅在布尔值更改时触发一次。

我的代码可以轻松调整以包含useState以保存布尔 matchMedia 返回值,并使用它来有条件地渲染组件、触发操作等。

这段代码使用的是新的 React 上下文 API:

  import React, { PureComponent, createContext } from 'react';
  const { Provider, Consumer } = createContext({ width: 0, height: 0 });
  class WindowProvider extends PureComponent {
    state = this.getDimensions();
    componentDidMount() {
      window.addEventListener('resize', this.updateDimensions);
    }
    componentWillUnmount() {
      window.removeEventListener('resize', this.updateDimensions);
    }
    getDimensions() {
      const w = window;
      const d = document;
      const documentElement = d.documentElement;
      const body = d.getElementsByTagName('body')[0];
      const width = w.innerWidth || documentElement.clientWidth || body.clientWidth;
      const height = w.innerHeight || documentElement.clientHeight || body.clientHeight;
      return { width, height };
    }
    updateDimensions = () => {
      this.setState(this.getDimensions());
    };
    render() {
      return <Provider value={this.state}>{this.props.children}</Provider>;
    }
  }

然后,您可以在代码中的任何位置使用它,如下所示:

<WindowConsumer>
  {({ width, height }) =>  //do what you want}
</WindowConsumer>

不确定这是否是最好的方法,但对我有用的是首先创建一个商店,我称之为 WindowStore:

import {assign, events} from '../../libs';
import Dispatcher from '../dispatcher';
import Constants from '../constants';
let CHANGE_EVENT = 'change';
let defaults = () => {
    return {
        name: 'window',
        width: undefined,
        height: undefined,
        bps: {
            1: 400,
            2: 600,
            3: 800,
            4: 1000,
            5: 1200,
            6: 1400
        }
    };
};
let save = function(object, key, value) {
    // Save within storage
    if(object) {
        object[key] = value;
    }
    // Persist to local storage
    sessionStorage[storage.name] = JSON.stringify(storage);
};
let storage;
let Store = assign({}, events.EventEmitter.prototype, {
    addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
        window.addEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    emitChange: function() {
        this.emit(CHANGE_EVENT);
    },
    get: function(keys) {
        let value = storage;
        for(let key in keys) {
            value = value[keys[key]];
        }
        return value;
    },
    initialize: function() {
        // Set defaults
        storage = defaults();
        save();
        this.updateDimensions();
    },
    removeChangeListener: function(callback) {
        this.removeListener(CHANGE_EVENT, callback);
        window.removeEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    updateDimensions: function() {
        storage.width =
            window.innerWidth ||
            document.documentElement.clientWidth ||
            document.body.clientWidth;
        storage.height =
            window.innerHeight ||
            document.documentElement.clientHeight ||
            document.body.clientHeight;
        save();
    }
});
export default Store;

然后我在组件中使用该存储,有点像这样:

import WindowStore from '../stores/window';
let getState = () => {
    return {
        windowWidth: WindowStore.get(['width']),
        windowBps: WindowStore.get(['bps'])
    };
};
export default React.createClass(assign({}, base, {
    getInitialState: function() {
        WindowStore.initialize();
        return getState();
    },
    componentDidMount: function() {
        WindowStore.addChangeListener(this._onChange);
    },
    componentWillUnmount: function() {
        WindowStore.removeChangeListener(this._onChange);
    },
    render: function() {
        if(this.state.windowWidth < this.state.windowBps[2] - 1) {
            // do something
        }
        // return
        return something;
    },
    _onChange: function() {
        this.setState(getState());
    }
}));

仅供参考,这些文件已被部分修剪。

您不一定需要强制重新渲染。

这可能对 OP 没有帮助,但就我而言,我只需要更新画布上的 widthheight 属性(您无法使用 CSS 执行此操作(。

它看起来像这样:

import React from 'react';
import styled from 'styled-components';
import {throttle} from 'lodash';
class Canvas extends React.Component {
    componentDidMount() {
        window.addEventListener('resize', this.resize);
        this.resize();
    }
    componentWillUnmount() {
        window.removeEventListener('resize', this.resize);
    }
    resize = throttle(() => {
        this.canvas.width = this.canvas.parentNode.clientWidth;
        this.canvas.height = this.canvas.parentNode.clientHeight;
    },50)
    setRef = node => {
        this.canvas = node;
    }
    render() {
        return <canvas className={this.props.className} ref={this.setRef} />;
    }
}
export default styled(Canvas)`
   cursor: crosshair;
`
我知道

这已经得到了回答,但只是想我会分享我的解决方案作为最高答案,虽然很棒,但现在可能有点过时了。

    constructor (props) {
      super(props)
      this.state = { width: '0', height: '0' }
      this.initUpdateWindowDimensions = this.updateWindowDimensions.bind(this)
      this.updateWindowDimensions = debounce(this.updateWindowDimensions.bind(this), 200)
    }
    componentDidMount () {
      this.initUpdateWindowDimensions()
      window.addEventListener('resize', this.updateWindowDimensions)
    }
    componentWillUnmount () {
      window.removeEventListener('resize', this.updateWindowDimensions)
    }
    updateWindowDimensions () {
      this.setState({ width: window.innerWidth, height: window.innerHeight })
    }

唯一的区别是我在调整大小事件上对 updateWindowDimensions 进行去抖动(每 200 毫秒仅运行一次(以提高性能,但在 ComponentDidMount 上调用它时不会去抖动它。

我发现,如果您遇到经常安装的情况,则有时会使安装变得非常滞后。

只是一个小小的优化,但希望它能帮助到某人!

这个厨房里有很多厨师,但无论如何我都会把我的帽子扔进去。这些都没有使用requestAnimationFrame我认为这是性能最高的。

这是一个使用 React 钩子和requestAnimationFrame的示例。这也使用纯 js,没有任何像 lodash 这样的库(由于捆绑包大小,我不惜一切代价避免这样做(。

import { useState, useEffect, useCallback } from 'react';
const getSize = () => {
  return { 
    width: window.innerWidth,
    height: window.innerHeight,
  };
};
 
export function useResize() {
 
  const [size, setSize] = useState(getSize());
 
  const handleResize = useCallback(() => {
    let ticking = false;
    if (!ticking) {
      window.requestAnimationFrame(() => {
        setSize(getSize());
        ticking = false;
      });
      ticking = true;
    } 
  }, []);
  useEffect(() => {
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
 
  return size;
}

以下是显示它正在使用的要点:Img.tsx with useResize。或者,您可以在我的存储库中看到它 此处 了解更多上下文。

关于为什么你应该这样做而不是去抖动你的函数的一些资源:

  • 一个相关的 GitHub 公关,有很好的解释
  • 中等,但在模因墙后面
  • 深入的堆栈溢出答案

谢谢你来参加我的Ted Talk。

componentDidMount() {
    // Handle resize
    window.addEventListener('resize', this.handleResize);
}


handleResize = () => {
    this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight);
    this.camera.aspect = this.mount.clientWidth / this.mount.clientHeight;
    this.camera.updateProjectionMatrix();
};

只需要定义调整大小事件功能。

然后更新渲染器大小(画布(,为相机分配新的纵横比。

在我看来,卸载和重新装卸是一个疯狂的解决方案......

如果需要,下面是安装座。

            <div
                className={this.state.canvasActive ? 'canvasContainer isActive' : 'canvasContainer'}
                ref={mount => {
                    this.mount = mount;
                }}
            />

只是为了改进@senornestor的解决方案以使用 forceUpdate 和 @gkri 的解决方案来删除组件卸载时的resize事件侦听器:

  1. 不要忘记限制(或去抖动(调用以调整大小
  2. 确保在构造函数中bind(this)
import React from 'react'
import { throttle } from 'lodash'
class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.resize = throttle(this.resize.bind(this), 100)
  }
  resize = () => this.forceUpdate()
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}

另一种方法是只使用"虚拟"状态而不是forceUpdate

import React from 'react'
import { throttle } from 'lodash'
class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.state = { foo: 1 }
    this.resize = throttle(this.resize.bind(this), 100)
  }
  resize = () => this.setState({ foo: 1 })
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}

必须将其绑定到构造函数中的"this"才能使其与类语法一起使用

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.resize = this.resize.bind(this)      
  }
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
}

谢谢大家的回答。这是我的 React + Recompose。它是一个高阶函数,包括组件的windowHeightwindowWidth属性。

const withDimensions = compose(
 withStateHandlers(
 ({
   windowHeight,
   windowWidth
 }) => ({
   windowHeight: window.innerHeight,
   windowWidth: window.innerWidth
 }), {
  handleResize: () => () => ({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  })
 }),
 lifecycle({
   componentDidMount() {
   window.addEventListener('resize', this.props.handleResize);
 },
 componentWillUnmount() {
  window.removeEventListener('resize');
 }})
)

https://github.com/renatorib/react-sizes 是一个 HOC 来做到这一点,同时仍然保持良好的性能。

import React from 'react'
import withSizes from 'react-sizes'
@withSizes(({ width }) => ({ isMobile: width < 480 }))
class MyComponent extends Component {
  render() {
    return <div>{this.props.isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
  }
}
export default MyComponent
<</div> div class="answers">

试试这个:-

resize = () => this.forceUpdate()
componentDidMount() {
  window.addEventListener('resize', this.resize)
}
componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}

出于这个原因,如果你使用CSS或JSON文件数据中的这些数据,然后用这个数据设置新的状态,this.state({width:"some value",height:"some value" }(;或者编写代码,如果你想响应式显示图像,在自我工作中使用宽度屏幕数据

import React, {useState} from 'react';
type EventListener = () => void
let eventListener: EventListener | undefined;
function setEventListener(updateSize: (size: number[]) => void){
    if(eventListener){
        window.removeEventListener('resize',eventListener);
    }
    eventListener = () => updateSize([window.innerWidth, window.innerHeight]);
    return eventListener as EventListener;
}
function setResizer(updateSize: (size: number[]) => void) {
    window.addEventListener(
        'resize',
        setEventListener(updateSize)
    );
}
function useWindowSizeTableColumns() {
    const [size, setSize] = useState([
        window.innerWidth || 0,
        window.innerHeight || 0
    ]);
    setResizer(updateSize);
    return size;
    function updateSize(s: number[]) {
        if(size.some((v, i) => v !== s[i])){
            setSize(s);
        }
    }
}
export default useWindowSize;

in index.js:

function render() {
  ReactDOM.render(<App />, document.getElementById('root'));
}
render();
window.addEventListener('resize', render);
重新

渲染强制重新计算依赖于 React 本身无法检测到的变量的所有内容,例如 window.innerWidth/innerHeight、localStorage 等,同时保持应用程序状态相同。

如果需要在其他情况下重新渲染,也可以导出此 render(( 函数并在其他地方使用它。

我不确定这对性能的影响(因为它可能会重新渲染所有内容或只是更改的内容(,但对我来说,调整大小时似乎足够快。

对于任何寻求 Typescript 支持的人,我根据开发人员的回答编写了@Lead以下钩子:

import { useState, useEffect } from 'react';

export const debounce = <A extends unknown[]>(callback: (...args: A) => unknown, msDelay: number) => {
    let timer: NodeJS.Timeout | undefined;
    return (...args: A) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
            timer = undefined;
            callback(...args);
        }, msDelay);
    };
};
export const useWindowDimension = (msDelay = 100) => {
    const [dimension, setDimension] = useState({
        width: window.innerWidth,
        height: window.innerHeight,
    });
    useEffect(() => {
        const resizeHandler = () => {
            setDimension({
                width: window.innerWidth,
                height: window.innerHeight
            });
        };
        const handler = msDelay <= 0 ? resizeHandler : debounce(resizeHandler, msDelay);
        window.addEventListener('resize', handler);
        return () => window.removeEventListener('resize', handler);
    }, []);
    return dimension;
};
export type Dimension = ReturnType<typeof useWindowDimension>;

好处显然是打字稿支持,还有控制延迟时间的选项(以ms为单位(,默认为 100ms 。如果延迟时间为0 or less,则不会对处理程序进行去抖动。
另一个区别是钩子返回一个类型为 Dimension 的对象,即:

type Dimension = {
    width: number;
    height: number;
}

这样每个人都清楚钩子实际返回的内容。