react hooks使用_使用React Hooks动态管理间隔

react hooks使用

I’ve come across a few blog posts, such as this one, about how to manage setInterval with hooks when your functional component mounts. This works if you want to set it and forget, but for my case I needed something a bit more dynamic. I’m converting a vanilla JS photo booth app I made to a react app, and one of the features involves taking a “burst” of photos.

我遇到了一些博客文章,例如本篇文章,内容是关于在安装功能组件时如何使用钩子管理setInterval 。 如果您要设置它而忘了它,则可以使用此方法,但就我而言,我需要更动态的东西。 我将我制作的香草JS 照片展位应用程序转换为React 应用程序,其中一项功能涉及“突发”照片。

Image for post
Quarantine hair has taken on a life of its own.
隔离的头发已经过了自己的生活。

This feature allows the user to press a button and take a certain number of pictures at a certain rate, both of which are are controlled by the user. The component that manages this feature was originally a class component but I wanted to convert it to a functional one.

此功能允许用户按下按钮并以一定速率拍摄一定数量的图片,这两种图像均由用户控制。 管理此功能的组件最初是一个类组件,但我想将其转换为功能组件。

So how does the burst feature work? Let’s take a look at the onBurst function in the original class component.

那么爆发功能如何工作? 让我们看一下原始类组件中的onBurst函数。

onBurst = () => {
var count = 0
var interval = setInterval(() => {
this.onScreenshot()
count++
if (count === this.props.settings.burst){
clearInterval(this.state.interval)
this.setState({interval: null})
}
}, this.props.settings.burstRate * 1000) this.setState({interval})
}

This function creates an interval at which to take at screenshot. We need to take this.props.settings.burst number of screenshots. this.props.settings.burstRate is an integer meant to represent seconds. We multiply burstRate by 1000 to convert the seconds to milliseconds. We increment a count variable at each interval to keep track of when to stop taking pictures (when count === burst). Once we reach this point, clear the interval (which is stored in state) and then set this.state.interval to null. Storing the interval in state allows the component to keep track of the interval so it can later be cleared.

此功能创建一个间隔以进行截屏。 我们需要this.props.settings.burst数量的this.props.settings.burst屏幕截图。 this.props.settings.burstRate是一个整数,表示秒。 我们将burstRate乘以1000,将秒转换为毫秒。 我们在每个间隔增加一个count变量,以跟踪何时停止拍照(当count === burst )。 一旦达到这一点,清除间隔(存储在state中),然后将this.state.interval设置为null 。 将间隔存储为状态允许组件跟踪间隔,以便以后可以清除它。

At each interval, we execute this.onScreenshot() which is responsible for actually taking the picture. See below.

在每个间隔,我们执行this.onScreenshot()负责实际拍摄照片。 见下文。

onScreenshot = () => {
this.setState({showScreenshot: true}, () => {
var video = document.querySelector(‘#video’)
var canvas = document.createElement(‘canvas’)
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext(‘2d’).drawImage(video, 0, 0);
var dataUrl = canvas.toDataURL(‘image/png’);
this.props.addImage(dataUrl)
})
}

*Note: I know the use of document... is an anti-pattern in react but I have been unable to find a way of achieving the desired results without doing so. If you know of one let me know in the comments!

*注意:我知道 document... 的使用 document... 是React的反模式,但是如果不这样做,我一直找不到找到所需结果的方法。 如果您知道一个,请在评论中告诉我!

This function uses the Canvas API to capture the live video’s data and create a dataUrl from it. This dataUrl is stored in the reducer, which is then used in my component to render the new image. For more on how the app captures and manages images, check out my blog post.

此函数使用Canvas API捕获实时视频的数据并dataUrl创建dataUrl 。 此dataUrl存储在reducer中,然后在我的组件中使用它来渲染新图像。 有关该应用如何捕获和管理图像的更多信息,请查看我的博客文章

This worked perfectly in my class component, but when I tried to manage this logic using react hooks, the effect was not the same. Either the interval would never end, resulting in an endless stream of images being rendered in my app, or the interval wouldn’t start, resulting in one sad, lonely image. I initially tried storing the interval using useState.

这在我的类组件中完美地工作,但是当我尝试使用react钩子管理此逻辑时,效果不一样。 间隔将永远不会结束,导致在我的应用程序中渲染出无尽的图像流,或者间隔将不会开始,从而产生一个可悲的孤独图像。 我最初尝试使用useState存储间隔。

const [interval, setMyInterval] = useState(null) 
//equivalent to this.state.interval

And my revised onBurst looked like this.

我修改过的onBurst看起来像这样。

const onBurst = () => {
var count = 0
var newInterval = setInterval(() => {
onScreenshot()
count++
if (count === burst){
clearInterval(interval)
setMyInterval(null)
}
}, burstRate * 1000)
setMyInterval(newInterval)
}

The reason why this wasn’t an effective use of useState is because my component was already being re-rendered when my props were updated following the addition of the new image. It didn’t need to be re-rendered by useState. I was able to determine in useEffect that my interval does get cleared (at least the state value for interval does) but the window interval didn’t get cleared.

之所以不能有效地使用useState是因为在添加新图像后更新我的道具时,我的组件已经被重新渲染。 它不需要由useState重新呈现。 我能够在useEffect中确定我的间隔确实被清除了(至少interval的状态值被清除了),但是窗口间隔没有被清除。

Image for post

My theory about why this doesn’t work is that there is some sort of memory leak in between the state/prop changes which are causing too many renders. This might be why the component does keep track the clearing of the interval. After doing a little digging, I found a post on Stack Overflow that made use of another react hook, useRef.

我的理论关于为什么不起作用的原因是,状态/属性更改之间存在某种内存泄漏,导致过多的渲染。 这可能就是组件确实跟踪间隔清除的原因。 经过一些挖掘之后,我在Stack Overflow上找到了一个帖子,该帖子利用了另一个react钩子useRef

useRef allows us to store and update data in the component without triggering a re-render. Now the only re-render happens when the props are updated. According to this post by Dan Abromov, useRef() allows us to better save the latest interval callback in useRef’s current property. useRef’s efficient ability to preserve data makes it the better hook choice for remembering the state of timing functions. We can store interval in a ref like so.

useRef允许我们在组件中存储和更新数据,而无需触发重新渲染。 现在,只有在更新道具时才进行重新渲染。 根据Dan Abromov的这篇文章useRef()允许我们更好地将最新的时间间隔回调保存在useRefcurrent属性中。 useRef的有效数据保存能力使其成为记住计时功能状态的更好的挂钩选择。 我们可以像这样将interval存储在ref

const interval = useRef(null);

Now that we have interval stored as a ref, let’s update onBurst to reflect these changes.

现在我们已将时间间隔存储为ref ,让我们更新onBurst以反映这些更改。

const onBurst = () => {
var count = 0
interval.current = setInterval(() => {
onScreenshot()
count++
if (count > burst){
clearInterval(interval.current);
}
}, burstRate * 1000)
}

With the above logic, we should be able to access the interval in our ref object (interval.current) and clear it when count has been incremented to be greater than burst. Let’s see the final result.

使用上述逻辑,我们应该能够访问ref对象中的interval.current ( interval.current ),并在count增加到大于burst时清除它。 让我们看看最终结果。

Image for post
Whether the camera loves me or not the burst works useRef ftw
相机是否爱我连拍都可以使用参考ftw

The interval is set, executed, and then cleared as it should be. For more on when to use useState or useRef, check out this post. The source code for the react photo booth app can be found in this GitHub repo (The old class component is Capture.js, the updated functional one is CaptureFunctional.js).

间隔应该设置,执行然后清除。 有关何时使用useStateuseRef ,请查看此文章 。 react照相亭应用程序的源代码可以在此GitHub存储库中找到(旧类组件是Capture.js ,更新的功能是CaptureFunctional.js)

翻译自: https://medium.com/swlh/dynamically-manage-intervals-with-react-hooks-2f6d75d9d31d

react hooks使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值