Now that we've managed to create a note and be able to add it to our back end, the next step is to 1
be able to take these notes that we've stored and be able to serve it back up onto the front end. 2
So there's a lot of this back and forth going on, but it's all done in order to be able to persist 3
our data across time. 4
So as I mentioned, at the moment, whenever we create a note and we refresh our front end, it disappears. 5
But that actually does, in fact, get added into our back end. 6
And you can see now our notes list contains a Note1 and also this mumbo jumbo that I just typed in 7
just now. 8
So how can we get that back out and to be able to display it in our app? 9
Well, the first thing we're going to do is we're going to work on our back end and we're going to create 10
a new function. 11
Now, it's going to be a public function. 12
But remember, when we're reading from the ICP blockchain, it's done a lot faster when we don't need 13
to modify anything. 14
And because all we're doing is just reading this list of notes, we can add that query keyword to 15
speed the whole thing up a lot more. 16
We're creating a public query function and I'm going to call it readNotes, and it's not going to accept 17
any inputs, but it is going to return asynchronously and going to give us not a list of notes, but 18
actually an array of notes. 19
So an array is another object in Motoko code that we can use, and structurally it's a lot more similar 20
to the arrays that we've got in JavaScript. 21
If we return an array of notes, then we'll be able to work with it much more easily when we get hold 22
of it in our JavaScript. 23
Now, why are we storing it as a list, though? 24
Because in a lot of the operations we're doing, when we're pushing notes onto it or when we're finding 25
a note to delete later on, working with something that's an array which is serialized is not very efficient 26
on the blockchain. 27
So when we're reading it, we're going to get hold of our lists. 28
So we're going to return our notes, but we're going to convert it into an array. 29
So we're going to use a function from our list documentation that I found which is called toArray, 30
and this will convert a list to an array. 31
There was quite a lot of digging around in order to find these because as you can tell, going through 32
the documentation, the Motoko programming language is still not yet fully featured, 33
so it's not got as many utility functions as, say, JavaScript or Python. 34
But we can make it work. 35
That's what we're going to use. 36
We're going to say list.toAarray. 37
And this takes just one argument, which is the list that you want to convert, and it's going to return 38
it in the format of an array. 39
We're going to pass on notes into here and we're going to return that as an array of note objects. 40
So the Motoko side is pretty simple, but now we have to address how do we get hold of it when our page 41
loads up? 42
Because when we add a new note, the set note method makes sure that this note's constant is updated. 43
And because we're using the useState hook, that means whenever it's updated, it's going to re-render 44
the screen and be able to draw on the new note that was created. 45
But now how do we tap into the moment when the first render is done, 46
when our website first loads up? 47
Well, in order to do that, we have to use another React hook. 48
And this is called the useEffect 49
hook. useEffect is triggered every time the render function gets called in React. 50
So every time the React component re-renders. 51
So in this case, it's the app component. 52
And of course that means the first time when the website is rendered to be displayed, it will also 53
be triggered. 54
So let's go ahead and add this to our code so that we can trigger a function whenever the app component 55
is re-rendered. 56
This function useEffect takes two parameters. 57
The first parameter is the function that should be called whenever the re-rendering happens 58
and I'm going to use the shorthand with my fat arrows that comes from ES6. 59
And this function takes no inputs and no outputs, but it is going to do something. 60
So first, just to assure ourselves that it's actually working, 61
let's first console.log when the useEffect is triggered. And let's save it, 62
let's go back to our Keeper app and open up our console. 63
You can see that useEffect is triggered already because it's been reloaded. 64
So if I reload this website you can see that line gets logged. 65
So in that moment, it would be great if we could also pull out the array of notes from our Motoko code. 66
Inside our useEffect, 67
we're going to call a function that we're going to create, which is going to be called fetchData. 68
And the reason why I'm splitting out this fetchData is because this fetchData has to be asynchronous 69
and useEffect can't really be turned into an asynchronous function itself. 70
Let's create this async function 71
fetchData. 72
And let's make it get hold of our data from our Motoko code. 73
So we're going to tap into our dkeeper canister 74
and we're going to call that method that we created here called readNotes. 75
And this then should be able to return us that array which we can store in a local constant, which 76
we can call the notes array. Because this is going to be returned asynchronously, 77
we have to wait on it. 78
So we have to add the await keyword in front of this method call so that we wait for this to come 79
back before we continue. 80
Now, whenever our window loads, it's going to trigger the console.log but also our fetchData. 81
And then this method is going to wait until it gets that array back. 82
And once it gets it though, we want to be able to update our notes using the update method setNotes. 83
So just below this constant, I'm going to say setNotes. 84
And inside I'm just going to say set everything to the new objects inside the notes array. 85
And that is going to trigger a re-render because our state updated. 86
So you can imagine that we could get ourselves very quickly into an infinite loop here, 87
right? 88
Because we have setNodes which is going to update the state of our nodes constant, which then is 89
going to trigger a rerender of our app React component. 90
And as we mention, every time the app component gets re-rendered, it triggers the useEffect. 91
So this could go on forever and we can see this happening when we console.log. 92
If we go ahead and run dfx deploy, 93
you can see that useEffect is triggered many, many, many times. 94
And when we want to add a newNote, you can see that it sort of works 95
but useEffect is triggered so many times because it's in this endless loop. 96
So what we need to know is that the useEffect hook actually has a second parameter, and the second 97
parameter is an array where we can specify a particular prop or some sort of variable which it checks 98
to see if it's changed before it triggers the function in useEffect. 99
So we can use this second parameter to prevent the useEffect from triggering more than once by putting 100
in an empty array. 101
And this will ensure that useEffect will run only once. 102
So after this first argument which is our function, we're going to add a comma and add an empty array 103
so that it stops once this has completed once, which is all we want because we only want this to 104
happen when the re-render or when the website reloads. 105
So now let's hit save. 106
Let's go back to our Keeper app and you can see that useEffect is triggered once. 107
But if we go ahead and take a note and click add, you can see the note is added in there. 108
But now because these notes are created on the front end and persisted on the back end, if we hit reload, 109
they don't disappear because they're being read from our Motoku backend on the blockchain and they're 110
being pulled out each time we reload. 111
You can write as many notes as you want, and now they'll actually stay on the screen. 112
Now, there's just one thing that you might have noticed, which is the order 113
the notes go in. 114
So if I go ahead and add note5 which will be the latest note, if I click add, you see it gets 115
added at the end here. 116
But then when I click refresh, because of the way our push function works by pre-pending a value, 117
so the new value gets added to the beginning of the list, then it kind of messes up the order every 118
time we refresh. 119
So how can we fix this? 120
Well, it's actually quite a nice thing that new notes get added to the front, I think, because that's 121
how it normally works on Google Keep 122
anyways. If you imagine you had a list of loads of these notes and it gets added somewhere at the bottom, 123
you won't even see it. 124
It should actually be in reverse chronological order in order to make sense. 125
The only thing we have to change then is when we add it to the frontend, 126
remember that it updates because setNotes triggers a update in the state. 127
And here we're saying let's get hold of the previous notes and let's create this newNote and we're 128
taking the newNote and adding it to the end of all of the previous notes. 129
So if we just simply reverse this array, 130
so we say, let's add the new note to the beginning, and then we'll add the previous notes at the end, 131
then we're going to get a different order, right? 132
So if we hit save, we go back and we add a new note, we'll call it note6 and click add, 133
you can see it now gets added to the very beginning. 134
And if we refresh our website, nothing happens. 135
None of the orders change and it works as we would expect it to. 136
So there you have it. 137
In this lesson, we've not only been able to persist our code and be able to read from our backend 138
using the ICP blockchain, we've also fixed our adding functionality so that the order that notes get 139
added in is more logical. 140
In the next lesson we've got the final thing we need to implement which is deleting these, because 141
at the moment you can see that I can leave them on the frontend and it works because we're updating 142
the state here using deleteNote, 143
but as soon as I hit refresh and that re-render gets triggered, it's going to pull the notes from my 144
backend and it's just going to reappear, which is not what we want. 145
Let's see how we can persist that in the next lesson.