- Read Tutorial
- Watch Guide Video
So let's work on that in this guide.
Now, you may have noticed we have at the top of our rich text editor component, we have a couple of imports that we're not using yet. And also, over this weekend, I had a little bit of fun and added a few more extensions to Visual Studio Code, so if you're wondering why it looks a little bit different, it's because I added those.
It has nothing to do with the code I write or anything like that, it's simply some fun things, like changing up icons and things like that. In the show notes, I'm gonna give you a link of the guide I went through, it has some odd types of tips and tricks for customizing your Visual Studio Code experience, so I'll include that if you wanna have some fun in setting up your environment as well.
Now, with all of that being said, let's jump into the code and what we're gonna have to build out. Now, the first thing that we need to do is, as you may have noticed, our rich text editor has a little bit different kind of state than we're used to working with.
Usually, we work with strings, sometimes arrays, sometimes objects, but we don't ever work with an actual function, or we haven't yet, but because we're working with Javascript and state is simply an object, objects can have keys that hold functions, and that's exactly what we're doing here.
So, let's take a look at this. Before we do anything at all, let's see what this looks like inside of Chrome. I'm gonna open up the React dev tools, and let me shrink this down just a little bit, and I'm gonna open up a new modal, go to the React dev tools, and then from here, select the rich text editor, so it might take you a little bit of time just to find this.
We don't want the draft editor block that is a nested component inside of draft.js, and so keep on scrolling up until you actually find our actual component, which is, there we go, RichTextEditor. Now, if you look at the state here, and you open this up, you can see that this is not a string.
This is not a normal type of value that we can work with, and so because of that, we're gonna have to perform a few other tasks in order to reach into our editor state and grab the value, and the reason why I'm spending some time right now discussing this is because I don't want you to think that we're spending time on something that doesn't matter. This is all very necessary, and it might feel a little complex in the beginning, but the more you go into it, hopefully, it starts to make more and more sense.
So now that we know that we're not working with our normal editor state, let's switch back and let's see what we need to do in order to update that. We're first gonna have to create a function, and I'm gonna call it onEditorStateChange, and bind it to this, so I'm gonna say this.onEditorStateChange and we'll bind it to this.onEditorStateChange, bind it to this, and that's all we need to do in the constructor, and now let's create this.
So now, I'm gonna go right above the render function, and call onEditorStateChange. This is going to take the editor state as an argument, and what you'll see here in a second is that we're going to take this function, and we're actually going to pass it as a prop directly into the editor.
Remember that we created this component and then we're simply calling draft.js from inside of it, so we have this nested component of editor. It has the ability for us to pass in our own props, and it has a prop called onEditorStateChange. So this naming right here, it's not required that you use this exact name, however, you are gonna have to use that name for the prop that we're gonna pass in.
So we pass in editorState as the argument, and then what is going to happen is, when we call this, and we can do it right now, if you come down into the editor component, I'm gonna say that I wanna pass a new prop. This is a function, and it is expecting it, so if you open up the documentation for draft.js and you go into its editor component, you will see that it gives you the ability to pass in a function as a prop, and say that we're calling this.onEditorStateChange, and then it's going to automatically pass in that argument, just like we did with our blog form component, even though our blog form, this handleRichTextEditorChange, even though it does take an argument, because we're simply passing in the function without calling it, we don't have to add in the argument here at the end yet.
So I'm gonna hit Save here, and so now we're passing this function. It is going to, every time someone starts typing into our rich text editor, it's going to be called and it's going to start triggering a process, so now that we have that, now we can update this function.
The first thing that we're gonna do is call this.setState, and then, we're gonna use the same pattern that we used in the blog form, so editorState is both the name of the key in our state, and it is also the argument that we have here. So if you remember what we can do is pass in a single item into the curly brackets as the object and that's all that has to get passed in, this is gonna update the editor state.
Now, this is not enough to get this working. One, we need to call the blog form, and we need to pass data back, but we also are about to learn about something kind of tricky with setState. Now, you may think, because this is how we've used it for this entire course, you may think that we can say this.setState, and then right afterwards, call this.props dot, and then call our method, which I'm just gonna copy, handleRichTextEditorChange, here, and then pass in the content itself, so you could pass in editorState.
This won't work for a few reasons. One, editorState, like we've already seen, is not a string, and we talked about in the last guide how important it was that our handleRichTextEditorChange, that only accepts a string, but we've already seen that it's a function.
Even when the user starts typing content in, it's still only gonna be a function, so we're gonna have to reach in, and we're gonna have to work with that data. We're gonna have to call some other functions to really massage that data and to parse out what we're looking for. We'll do that in a second, but there's another little issue here, setState is actually an asynchronous event. So what that means is that we cannot be confident that when we call this.setState, that it will be called immediately.
There can be a little bit of a delay, and that can lead to a lot of tricky behavior, so imagine a scenario where we call this.setState, we're updating the editorState, but it is delayed. It might only be, and we'll do a little visualization here. Let's say that it takes .05 milliseconds, it's very fast, but it's not immediate.
However, if this takes any amount of time, and we then call this.props.handleRichTextEditorChange, and we pass in the state, we are going to be in a situation where our state does not line up, because our component state has not updated quite yet, but we've still sent up the updated version, or what we thought was the updated version, to our prop.
Now, this might seem a little convoluted, and this definitely falls into the non-trivial range, but if you do not understand this, it will lead to quite a few bugs, not just in this feature, but in future features that you build, this.setState is not something that you can trust to happen immediately.
So if you're trying to do something like pass props up to another component, you need to wait till it's done. How do you do that? Well, this.setState, if you look at the documentation, has a little trick up its sleeve. It is that you can pass in a second argument.
Up until this point, this.setState has only, we've only passed in a single object, which is the new version of the state. What you can do, though, is if you pass in a second argument, you actually have the ability, so if you give a comma after the curly brackets, you have the ability to say, we want you to wait until state has been updated, and then, I want you to do other stuff.
So what this does, is it allows us to protect ourselves and we can be assured that this.setState here, this entire process, will have completely finished and then we can trust that that data is going to have all of its integrity. It's not going to give us two different versions of what we'd expect from the content, and then from there, this is where we can call our function.
So, let's do that now, so I'm gonna take this.props.handleRichTextEditorChange, and I'm gonna make this the second argument, and I'll get rid of our comment here, and now if I hit Save, it'll get nice and prettified for us.
Now, this is still not perfect, we at least have fixed our asynchronous issue. We make sure that we are not trying to update our parent state until the local state's been updated, but remember, our editor state is still in the situation where it's passing in a function, so this is where our other values here, like draft to HTML, and convertToRaw, some of these other functions that we have that we've not used yet, this is where they come into play, so let's walk through that now.
So we can't trust our editor state, or we can't work with it as a string, so what we can do, though, is we can call a few other processes, and I'm gonna put this on one other line. You could all put it on the same line if you'd like, but this makes it a little easier for you to read.
So the first thing we need to do is call draftToHtml, what this is going to do is it's going to take the draft.js content and it's going to look through it and it's gonna check to see, okay, this is HTML code. I'm going to take that, and I'm gonna make it into a string, so this is what allows us to take all of that code that we enter in, or the content we type into the editor, and it's going to allow it to be parsed as HTML, so then we can render it and have all the cool formatting things. So that's one part of it.
The next thing is, because we have that object, we have to run a couple of processes on it. One is there is a higher order function called convertToRaw, which we've already imported. This is a function that takes in the code, it takes in the editor code changes, and it parses it into something that we can actually work with. It takes it and it puts it into a raw string, draftToHtml takes that, then, and then wraps it up into HTML string that we can work with.
Is that a little confusing? This is why I broke this into a few videos, there's a lot going on here. Don't worry, we're gonna circle back afterward just to make sure it's clear, but just know that this is a few set of processes. It's a few nested requirements for us to take that code, all that stuff that we typed in, to the text editor, and to allow it to just be treated like a normal string.
So now that we have that, we can call this.state.editorState, which we now know has been updated if the user is typing things in, and we have a number of functions that this gives us, and if I hit dot, you can see that Visual Studio Code pulls in all of these functions, or at least it should. You can see it right here on the screen, so we have all of these functions that are built into Visual Studio Code.
You have the one we're about to use, which is getCurrentContent. You could get current inline styles, you could get the decorator, you could get all of these things. You probably may never use any of these, except for getCurrentContent, but that's just to show you what is available, and this is a function, so make sure that you call it with parentheses.
I'm gonna hit Save here, and let's just review what we've done. We have created a new function called onEditorStateChange. This is passed to the editor as a prop, and it expects that, that's not something that we did or anything that we made up, the draft.js component itself is expecting this prop. Whenever someone starts typing in, this prop is going to be called. They're gonna run the function, they're gonna pass in the new editor state.
From there, we're updating the rich text editor's state value, and then, because we're using this state and passing in a comma, this gives us the ability to ensure that this next process of calling our blog form, that it does not get run until we have actually updated the local state here. From there, we take our content, we convert it to HTML, after it has been, we have gotten the current content, and we've converted it into something we can actually work with.
We have taken out all of the metadata and all of those other functions, and everything like that, and we turned it into a plain string, and then we have that where it can be used as HTML. So quite a bit's going on there. Let's test this out to make sure it's working, so I'm gonna open up Visual Studio Code, I'm gonna hit Refresh, and let's go and let's take a look and open up the modal. Now, if I select the blog form, let's come and select the actual form itself.
Let's take a look at the state, so right here, you can see we have blog status, content, and title, and if I update these to be any kind of value, and then start typing in, notice, as soon as I clicked into the rich text editor, it immediately gave us paragraph tags, well, that's pretty cool. Let's start typing something else, like Something else, just like this.
And notice here, how our content has been updated, and if you wanted to do something like highlight this, and make it bold, and you can see right there, it added strong tags, and let me give us a little more room, so you can see all of this happening in real time, and if I wanted to take else and make it underlined, each time, the code is getting updated.
And you notice, we're not writing HTML code. What's happening behind the scenes, the reason why we had to call all those processes, like convert to raw and draft to HTML, is because, behind the scenes, this text editor is injecting the HTML code. They saw that we added an underline, so they updated the tags inside of that string. If we were to add anything else, such as taking it and making this an h3 heading, you can see this is now no longer a paragraph tag. It is an h3, and all of that appears to be working.
Now, if I hit save here, we have not wired all of this perfectly up, but if I hit Save, you'll see that it has been updated, and we have our temporary, we have that new value, we have our H3, we have strong, everything there is working perfectly. We have a few cleanup items to do in the next guide, but I want you to, if this is working, take a moment and really appreciate everything that we've done right there.
Notice how we have taken a very popular library like draft.js and we've integrated it directly into our application. We worked with all kinds of components, such as higher order components, to be able to write content and have it automatically converted to HTML.
I can promise you that if you were to look back, back to when I started developing, 15 some years ago, that process right there, that might have taken us an hour or so to build out, that would've taken weeks to build out, years ago. So, just appreciate that right now, is what we have here is, we've added some pretty cool behavior, and giving you the ability to have your own version of a rich text editor, right in your own portfolio, that's pretty cool.
So now that we've done that, in the next guide, we're gonna add a few cleanup items. We're gonna update some other parent state, and we're going to clean it up so that we can continue working with it.