Check out a free preview of the full React Native, v3 course
The "Persisting Countdown State" Lesson is part of the full, React Native, v3 course featured in this preview video. Here's what you'd learn in this lesson:
Kadi demonstrates how to store and retrieve data from async storage, and also shows how to cancel scheduled notifications. Kadi also discusses the use of useEffect and useState hooks to handle the fetching and updating of the countdown state.
Transcript from the "Persisting Countdown State" Lesson
[00:00:00]
>> Speaker 1: So the next thing we're going to do is persist the state. So we've seen this done already with async storage. We're going to do this again with async storage. Originally this, I intended to set the timestamp with date now instead of seconds, so I used milliseconds here.
[00:00:17]
But if you ever need to have a second, minute, hour, day, or whatever in milliseconds, then this is a nice reference for it. So one second is 1000 milliseconds, one minute is 60 times 1000 milliseconds, and then one hour is 60 minutes, one day is 24 hours, etc.
[00:00:41]
So let's see what we actually want to store in our Countdown Storage State. So I'm gonna create a type called PersistedCountdownState. And this is the stuff that we want to store in our acing storage. So one thing about notifications is that once you trigger a notification, so I'm setting a notification to be in the future.
[00:01:11]
And unless it happens or I cancel it, unless I cancel it, so I'm sending notification to be in the future. And unless I cancel it will get triggered even though I've set a new notification. So I want to make sure that I'm storing the idea of the notification that I created so I could cancel it in the future.
[00:01:32]
So the idea is that let's say that there's a thing that I need to do every hour and I do it after 30 minutes, so that was before my notification. So I want to cancel notification and then not get this notification because I've already done the thing. So with this we need to store the currentNotificationId.
[00:01:59]
Which will be a string or undefined. Undefined will be the initial state and for our history tab let's keep it simple for now and we'll just store the array of time stamps of the past times you completed it. So completedAtTimestamps.
>> Speaker 1: So this will be an array of numbers.
[00:02:32]
>> Speaker 1: Let's also do a storage key. So const CountdownStorageKey. So this will be the key that we're gonna use in acing storage to store our countdown state against. So let's call it taskly-countdown.
>> Speaker 1: And this timestamp, let's call it frequency. So we're gonna hard-code this. So in the future, you could make this UI dynamic, so the user could create a task for themselves in the future.
[00:03:16]
So they could say that, I wanna do this every two weeks and here's the thing I wanna do, etc. But we're gonna just hard code this for now for simplicity. So this frequency will denote how often you need to do the thing. So I'm gonna set this to 10 seconds for the time being.
[00:03:36]
So that will be 10 times 1000, so this will be 10 milliseconds. So this is 10 seconds in milliseconds. And we're gonna hard code this for now, but in the future you could build a UI to let users have defined themselves. And I'm gonna leave it for 10 seconds for now because it will be easier to use for testing.
[00:04:08]
Now let's add another use state for the countdownState. So we'll do a CountdownState and set CountdownState. So this will be the persistent state that we're getting from async storage. So this will be useState. And we're gonna start with undefined, so we'll start with nothing. And it will be the persisted CountdownState.
[00:04:42]
And now, I'm glad another useEffect. So, you can have as many useEffects as you want, so you don't need to just have one. So in this case, because these are kind of separate interactions, let's do a separate useEffect.
>> Speaker 1: And let's do the same as we did with the shopping list.
[00:05:07]
So we want to initialize by fetching the countdown state from storage. So remember, you can't have async functions in useEffect. So we'll do const init, which will be an async function, an async error function. And this will do const value. And we're going to get the countdown state from storage.
[00:05:36]
So we'll use the utility functional created area, which will be getFromStorage. And we'll pass in the countdown storage key, which is this. And setCountdownState to be this value. And we'll call init. Now in our interval useEffect The timestamp will now depend on the frequency, right. So here in CompletedAtTimestamps, we're storing every time we've completed the things.
[00:06:21]
So let's say that we're going to store the last completed first. So we can do the lastCompletedTimestamp, And this will be the countdownState, completedAtTimestamps. So this might be undefined, so that's why in Touchscript Land we're gonna have a question mark here because it's safely getting the computed timestamps.
[00:06:55]
And we're gonna get the last compute, so this will be the first one on the list. So here in a useEffect, our timestamp will change. So the timestamp will depend on whether you've completed it before. So if it's completed before, Okay, so this timestamp will be when the thing is due by.
[00:07:30]
So if it's completed before, then we'll use this last completed timestamp, and we're going to add the interval or frequency of how often we want to do it. So that's why we had to define it in milliseconds. So let's do plus frequency. And otherwise, just to have a default, if you've never done this before, if you've never completed the thing before, we'll just default to date.now.
[00:08:02]
And lastly, so passing in anything in this array, in the use effect, do sparingly and really think it through. Because the way use effect works is it runs every time The value passed here in this arguments array is updated. So in our case, we want to run this every time this LastCompletedTimestamp changes.
[00:08:34]
So what will happen is when this changes, it will clear the interval and then run this again. And it's really important to do this sparingly because for example, if you had a set state inside the use effect and then also in the dependencies array then you would get into an infinite loop and that will be sad.
[00:08:59]
Let us hook up this schedule notification. So we call this schedule notification, but we want to also then update the countdown state when we do this. So. Okay? Let's do a let here for the pushNotificationId. And this schedule notification a sync returns a string, which is the pushNotificationId.
[00:09:41]
Now, let's update our title to say that the thing is due. And then for the trigger, so when we schedule the notification, we want it to happen when our frequency was. So we'll use this frequency. This frequency is now millisecond, in milliseconds, not seconds. So we'll say the trigger will be frequency divided by 1000 because this takes seconds.
[00:10:16]
And then, so this will be after this else, so we'll set our push notification. We want to update our state with the data that we just added. So first, and this is the important tidy up thing, we'll check our countdown state to see that if there was already a notification Scheduled.
[00:10:40]
So let's do countdownState currentNotificationId. So if this is not undefined, we will call await notifications and it's cancel scheduled notification. And actually notice that there is a function for cancel all skation notifications as well. So this might be handy in some scenarios. But in this case, we know our notification ID.
[00:11:07]
So we can cancel this particular notification. So we'll call cancel notification, async with notification ID. And now we'll need the newCountdownState. So const newCountdownState. So this will be the persisted countdownState that we created earlier. So we'll need to have the currentNotificationId. So this will be the push notification ID that we created here.
[00:11:53]
So this might be undefined, but that is fine. And completedAtTimestamp so this is our history of past times we've completed this. We need to also handle the case, the initial case where the countdown state didn't exist. So let's say that, if countdown state exists or when it didn't.
[00:12:22]
So when it didn't, we just do date.now to add the current timestamp. And if we already had a countdown state, then we want to spread the countdown state to complete our timestamps. And we want to add the new completion to the beginning of the array. So we'll add our date.now here.
[00:12:46]
Now let's set countdownstate. And await save to storage, which will need the countdown storage key and the new countdown state. Okay, that was a lot of code and let's hope it works. So we'll open this, so I've never done this before, so it's 0. So I'm hoping if I do this, it's going to start counting down and when it goes to one, we go, right, and then if I do the thing, it'll go back to count again.
[00:13:43]
And let's also check that, so let's go into the red. And I'm gonna close this completely and reopen it. And I'm hoping that it's in the red, which means that it's actually persisted. And let me show you on my Android phone. Okay, so initially I need to do it once, so it will start counting.
[00:14:19]
And now it's gonna count down. When we go into the red, which is excellent. So I didn't get a notification because I'm in the app. So let me say that I've done the thing and I background the app. So I'm expecting to get a notification in a couple of seconds saying that this saying is do.
[00:14:43]
Beautiful and the other thing that we want to check that the notification gets canceled if I do the thing before it's due. So let's say that I've done the thing and then I'm going to do it three more times before it gets to red. So I'm waiting a couple of seconds.
[00:15:07]
And then I background it. So even though I've now done it three times, I'm expecting to only get one notification because the other two should have been canceled. And I do so, that is the value of canceling the notification.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops