使用requestAnimationFrame
进行动画制作应该很简单,但是如果您没有仔细阅读React的文档,您可能会遇到一些令人头疼的问题。以下是我亲身经历的三个“陷阱”时刻。
简而言之: 将空数组作为useEffect
的第二个参数,以避免其运行多次;将函数传递给状态的setter函数,以确保始终拥有正确状态;使用useRef
存储时间戳和请求ID等内容。
useRef
不仅仅用于DOM引用
在函数组件中存储变量有三种方法:
- 我们可以定义一个简单的
const
或let
,其值将在每次组件重新渲染时始终被重新初始化。 - 我们可以使用
useState
,其值在重新渲染中保持不变,如果更改它,它也会触发重新渲染。 - 我们可以使用
useRef
。
useRef
钩子主要用于访问DOM,但它不仅仅是这个。它是一个可变对象,可在多次重新渲染中保持一个值。它与useState
钩子非常相似,只是您可以通过其.current
属性读取和写入其值,并且更改其值不会重新渲染组件。
例如,下面的示例即使组件被其父组件重新渲染,也会始终显示<samp>5</samp>
。
function Component() { let variable = 5; setTimeout(() => { variable = variable 3; }, 100) return <div>{variable}</div> }
……而这个示例会将数字不断增加三,并持续重新渲染,即使父组件没有改变。
function Component() { const [variable, setVariable] = React.useState(5); setTimeout(() => { setVariable(variable 3); }, 100) return <div>{variable}</div> }
最后,这个示例返回五,并且不会重新渲染。但是,如果父组件触发重新渲染,那么它每次都会有一个增加的值(假设重新渲染发生在100毫秒之后)。
function Component() { const variable = React.useRef(5); setTimeout(() => { variable.current = variable.current 3; }, 100) return <div>{variable.current}</div> }
如果我们有可变的值,我们希望在下一个或以后的渲染中记住它们,并且我们不希望它们在更改时触发重新渲染,那么我们应该使用useRef
。在我们的例子中,我们需要在清理时不断变化的请求动画帧ID,如果我们根据周期之间经过的时间进行动画,那么我们需要记住上一个动画的时间戳。这两个变量应该存储为引用。
useEffect
的副作用
我们可以使用useEffect
钩子来初始化和清理我们的请求,尽管我们希望确保它只运行一次;否则,它最终会在每次渲染时创建、取消和重新创建动画帧请求。这是一个有效的,但不好的例子:
function App() { const [state, setState] = React.useState(0) const requestRef = React.useRef() const animate = time => { // 根据动画更改状态 requestRef.current = requestAnimationFrame(animate); } // 不要这样做 React.useEffect(() => { requestRef.current = requestAnimationFrame(animate); return () => cancelAnimationFrame(requestRef.current); }); return <div>{state}</div>; }
为什么不好?如果您运行此代码,useEffect
将触发animate
函数,该函数将更改状态并请求新的动画帧。听起来不错,除了状态更改将通过再次运行整个函数(包括将首先作为清理取消上一周期中animate
函数发出的请求,然后启动新请求的useEffect
钩子)来重新渲染组件。这最终替换了animate
函数发出的请求,这是完全不必要的。我们可以通过不在animate
函数中启动新的请求来避免这种情况,但这仍然不是很好。它仍然会在每一轮留下不必要的清理,如果组件由于其他原因重新渲染——例如父组件重新渲染它或其他状态已更改——那么不必要的取消和请求重新创建仍然会发生。更好的模式是只初始化一次请求,通过animate
函数保持它们的旋转,然后在组件卸载时进行一次清理。
为了确保useEffect
钩子只运行一次,我们可以将空数组作为第二个参数传递给它。但是,传递空数组有一个副作用,它会阻止我们在动画过程中获得正确状态。第二个参数是效果需要响应的更改值的列表。我们不想对任何东西做出反应——我们只想初始化动画——因此我们有空数组。但是React会将其解释为这意味着此效果不必与状态保持最新。这包括animate
函数,因为它最初是从效果中调用的。结果,如果我们尝试在animate
函数中获取状态的值,它将始终是初始值。如果我们想根据其先前值和经过的时间来更改状态,那么它可能无法工作。
function App() { const [state, setState] = React.useState(0) const requestRef = React.useRef() const animate = time => { // 此处的“state”将始终是初始值 requestRef.current = requestAnimationFrame(animate); } React.useEffect(() => { requestRef.current = requestAnimationFrame(animate); return () => cancelAnimationFrame(requestRef.current); }, []); // 确保效果只运行一次 return <div>{state}</div>; }
状态的setter函数也接受函数
即使useEffect
钩子将我们的状态锁定到其初始值,也有一种方法可以使用我们最新的状态。useState
钩子的setter函数也可以接受一个函数。因此,与其像您大多数时候那样基于当前状态传递值:
setState(state delta)
……您也可以传递一个接收先前值作为参数的函数。而且,是的,即使在我们这种情况下,这也会返回正确的值:
setState(prevState => prevState delta)
将所有内容整合在一起
这是一个简单的示例,用于总结所有内容。我们将把上述所有内容放在一起,创建一个计数器,该计数器计数到100,然后从开头重新开始。我们希望持久化和变异而不重新渲染整个组件的技术变量使用useRef
存储。我们通过将空数组作为其第二个参数来确保useEffect
只运行一次。我们通过向useState
的setter传递一个函数来变异状态,以确保我们始终拥有正确状态。
更新:使用自定义钩子更进一步
一旦基础知识清楚了,我们也可以使用钩子进行元编程,通过将大部分逻辑提取到自定义钩子中。这将有两个好处:
- 它极大地简化了我们的组件,隐藏了与动画相关但与我们的主要逻辑无关的技术变量。
- 自定义钩子是可重用的。如果您在另一个组件中需要动画,您也可以在那里简单地使用它。
自定义钩子起初听起来可能是一个高级主题,但最终我们只是将代码的一部分从组件移动到函数,然后像任何其他函数一样在组件中调用该函数。按照约定,自定义钩子的名称应该以use
关键字开头,并且钩子的规则适用,但除此之外,它们只是我们可以使用输入进行自定义并且可能返回某些内容的简单函数。
在我们的例子中,为了为requestAnimationFrame
创建一个通用的钩子,我们可以传递一个回调函数,我们的自定义钩子将在每个动画周期调用该函数。这样,我们的主要动画逻辑将保留在我们的组件中,但组件本身将更加专注。
以上是使用React Hooks使用requestAnimationFrame的详细内容。更多信息请关注PHP中文网其他相关文章!

具有CSS的自定义光标很棒,但是我们可以将JavaScript提升到一个新的水平。使用JavaScript,我们可以在光标状态之间过渡,将动态文本放置在光标中,应用复杂的动画并应用过滤器。

互动CSS动画和元素相互启动的元素在2025年似乎更合理。虽然不需要在CSS中实施乒乓球,但CSS的灵活性和力量的增加,可以怀疑Lee&Aver Lee&Aver Lee有一天将是一场

有关利用CSS背景滤波器属性来样式用户界面的提示和技巧。您将学习如何在多个元素之间进行背景过滤器,并将它们与其他CSS图形效果集成在一起以创建精心设计的设计。

好吧,事实证明,SVG的内置动画功能从未按计划进行弃用。当然,CSS和JavaScript具有承载负载的能力,但是很高兴知道Smil并没有像以前那样死在水中

是的,让#039;跳上文字包装:Safari Technology Preview In Pretty Landing!但是请注意,它与在铬浏览器中的工作方式不同。

此CSS-tricks更新了,重点介绍了年鉴,最近的播客出现,新的CSS计数器指南以及增加了几位新作者,这些新作者贡献了有价值的内容。

在大多数情况下,人们展示了@Apply的@Apply功能,其中包括Tailwind的单个property实用程序之一(会改变单个CSS声明)。当以这种方式展示时,@Apply听起来似乎很有希望。如此明显


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

记事本++7.3.1
好用且免费的代码编辑器

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),