在窗口调整大小时重新渲染 React 组件
介绍
大多数情况下,我们都会尝试创建具有灵活 UI 的React应用,以响应可用的视觉空间。但有时这既不可能也不切实际。在这种情况下,当窗口或视口大小发生变化时,显式重新渲染 React 组件会很有用。
监听调整大小
React 没有内置调整大小事件,但我们可以从 React 组件内部监听原生浏览器窗口 调整大小事件:
import React from 'react'
function MyComponent() {
React.useEffect(() => {
function handleResize() {
console.log('resized to: ', window.innerWidth, 'x', window.innerHeight)
}
window.addEventListener('resize', handleResize)
})
return <div>w00t!</div>
}
此代码将简单地监听窗口调整大小事件并控制台记录类似“调整为:1024 x 768”的内容。
调整大小时重新渲染
但是当检测到 resize 事件时,上述代码不会重新渲染任何内容。我们仍然必须告诉 React 本身某些内容已发生更改,才能触发重新渲染。
在正常情况下,当组件的 props 或状态发生变化时,React 会重新渲染组件。为了在示例中触发MyComponent的重新渲染,我们将在检测到事件时在组件上设置内部状态:
import React from 'react'
function MyComponent() {
const [dimensions, setDimensions] = React.useState({
height: window.innerHeight,
width: window.innerWidth
})
React.useEffect(() => {
function handleResize() {
setDimensions({
height: window.innerHeight,
width: window.innerWidth
})
}
window.addEventListener('resize', handleResize)
})
return <div>Rendered at {dimensions.width} x {dimensions.height}</div>
}
现在我们已经设置了一些内部状态,dimensions,它具有高度和宽度属性。在handleResize中,我们不再只是console.log,而是在检测到调整大小时使用setDimensions设置新状态。如果我们只关心高度或宽度调整大小,我们可以只跟踪我们需要的内容。
此外,为了显示重新渲染确实正在发生,我们已将输出更改为打印类似“以 1024 x 768 渲染”的内容。
清理监听器
在添加事件侦听器(例如 resize 事件)时,我们应确保在添加后进行清理。到目前为止的示例中,我们还没有这样做,这可能会导致我们的应用程序在以后出现问题。
每当 React 感觉到需要时,它就会多次执行组件。每次重新渲染时,useEffect都会被再次调用。这将创建n 个新的handleResize事件绑定到 resize 事件。如果经常重新渲染此组件,则可能会在我们的程序中造成严重的内存泄漏。我们只需要或想要一个事件侦听器。如果我们总是在创建新事件侦听器之前清理已建立的事件侦听器,我们将确保只有一个侦听器。
React 为我们提供了一种使用 useEffect来实现这一点的方法。当将一个函数传递给useEffect时,如果该函数还返回一个函数,则将调用该返回的函数来执行任何所需的清理。我们可以将removeEventListener代码放在那里:
import React from 'react'
function MyComponent() {
const [dimensions, setDimensions] = React.useState({
height: window.innerHeight,
width: window.innerWidth
})
React.useEffect(() => {
function handleResize() {
setDimensions({
height: window.innerHeight,
width: window.innerWidth
})
}
window.addEventListener('resize', handleResize)
return _ => {
window.removeEventListener('resize', handleResize)
}
})
return <div>Rendered at {dimensions.width} x {dimensions.height}</div>
}
现在我们已经干净整洁了。
减少调整大小
目前,我们的示例代码设置为在窗口调整大小时调用handleResize 。我们会在事件循环允许的范围内设置状态并针对每个像素更改重新渲染。
但是,如果有充分的理由减少处理调整大小的频率呢?出于性能原因,我们可能希望在重新渲染时不那么激进,例如在组件渲染缓慢或成本高昂的情况下。
在这种情况下,我们可以对调整大小处理进行去抖动,从而重新渲染。这意味着在调用handleResize函数之间进行节流或等待。有可靠的去抖动实现。让我们在示例中添加一个简短而简单的实现:
import React from 'react'
function debounce(fn, ms) {
let timer
return _ => {
clearTimeout(timer)
timer = setTimeout(_ => {
timer = null
fn.apply(this, arguments)
}, ms)
};
}
function MyComponent() {
const [dimensions, setDimensions] = React.useState({
height: window.innerHeight,
width: window.innerWidth
})
React.useEffect(() => {
const debouncedHandleResize = debounce(function handleResize() {
setDimensions({
height: window.innerHeight,
width: window.innerWidth
})
}, 1000)
window.addEventListener('resize', debouncedHandleResize)
return _ => {
window.removeEventListener('resize', debouncedHandleResize)
}
})
return <div>Rendered at {dimensions.width} x {dimensions.height}</div>
}
请注意,我们将handleResize包装在debounce()调用中,并将其返回的新函数绑定到debouncedHandleResize变量。然后我们在事件侦听器设置和清理中都使用此变量。
debounce ()调用的第二个参数是 1000ms,这意味着我们确保handleResize代码每秒最多被调用一次。
结论
通过整合监听本机调整大小事件、清理这些事件绑定以及控制事件处理程序运行频率的能力,我们现在可以自信地重新渲染我们的 React 组件以响应任何视口调整大小事件。
要查看此代码的运行情况,请查看此运行示例。
了解更多
探索 Pluralsight 上的这些 React 课程以继续学习:
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~