Build a Browser Code Editor in React (Monaco React Editor)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's going on guys welcome back to another video today we're going to be building this in browser code editor and we're going to be able to write any sort of code in the browser and then execute it and see the output so if I add another console log here and click run code it'll execute the code and we see the output printed we also get a nice Intellis sense so if I declare an array and call the map method we'll see the description and all the parameters similar to how we see it in vs code we can also select different languages like python or Java and this works as well so I could just execute this code and that'll work now it's also going to print errors so if I just type in a variable that doesn't exist we'll see that error here then I could select a new language and rerun the code and see the output I'll be using the react Monaco Editor to build our code editor here we'll use chakra UI to style the app and then and the Piston API to execute our code and return the output we could do that all for free with no setup required so without further Ado let's get [Music] started all right so I created a react app using vet and then install chacker UI to get started I'm going to get rid of all these starting files and set up the chakra provider let's get rid of all these CSS files and then in our main. jsx we're going to wrap our entire app in a chaker provider let's import that at the top and then we need to import our theme so I created a theme here that's just going to enforce dark mode on our app and we need to import that and pass it in as a prop now if we hit save we should be able to see our app running in the browser so to start it up we need to run npm run Dev and then go to Local Host on Port 5173 and we should see our app running here now let's start styling our app container so in this box I'll give it a Min height of 100 VH I'll also set the background color to a custom color that I just picked out we'll set the text color to gray. 500 and give it some left and right padding of six units and then top and bottom padding of eight units so now if we save we should see those Styles being applied now let's create the code editor on the left half of our app under the source folder let's create another folder called components and in there we'll create a file called code editor. jsx now in here we're going to return a box from Chakra and now we need to import The Monaco code editor let's open up the docs for the Monaco code editor and it's called at monono editor/ react and now we can scroll down to see the example make sure to install the package if you haven't already and then in this code you can see that we just need to use the editor component and pass it a few props so I'm just going to go ahead and copy this and just paste it into our app let's make sure that import the editor from mono editor and now let's save and see what it looks like in our browser first we need to import the code editor into our app and now we should see it in our browser so here it is and our editor is working and now we can start writing some JavaScript the first thing I want to do is get rid of this horrible light theme to do that let's go back into our code editor and right below the height let's add a theme prop and this could be vs light or vs dark then let's decrease the height to 75 VH and now let's add some state to our editor so to store the value of this code that we're writing let's create a piece of state above we'll call it value and by default it'll be an empty string let's make sure to import use state from react and now let's pass this value in as a prop so the prop is called value and we want it to be equal to our value that's in the state now we need to add an onchange Handler and this will be a function that gets the value from the editor and then updates our state and we don't care about the event so we can get rid of that and now when we save we should see those updates here and as we type it'll still work as expected all right the next thing I want to do is focus the editor when it mounts so to do that we need to create a reference to the editor so above our state let's create a reference we'll call it editor ref and that'll be equal to use ref and now we need to define a function that will focus the editor when it mounts so we could pass an on Mount prop and this is going to be a function so let's define that function above we'll call it on Mount And as a parameter we get the editor so we could take that in and inside the function we're going to want to call editor. current equals editor so that's going to set the value of our reference to the editor passed in and then we're going to call editor. Focus now let's pass this on Mount function here and now if we refresh the page it should focus our editor and there we go now let's build out the language selector so let's build out this menu here where you can select a different language and it's going to update the starting code of our editor let's go back to our code and under components create a new file called language selector and in here we'll return a box and let's add a text element and we'll just say language then let's give it some margin bottom we'll set that to two units and set the font size to large below the text component I want to add a chakra menu so let's open up the docs and on the menu component let's scroll down to the example and this is basically what we want so let's go ahead and copy this code here and just paste it into our app and then update all these Imports and now let's get rid of this right icon prop because we don't need that and instead of actions we'll just say JavaScript for now and now within our menu list instead of just outputting items like this I want to create an array of languages and then just Loop through them and output them so under the source Direct directory let's create a file called constants and from here I'm going to export a constant called language versions and this is going to be an object with key value pairs where the key is the language and the value will be the version these versions that I've defined here are specific to what the Piston API accepts and I'll show you that later in the video now let's take this object and create an array of all of these keys and values so right at the top I'll just do const languages that's going to be equal to object. entries and then we're going to pass in the language versions object so this variable will now be an array of arrays where each item in the array will have the key and the value now let's take this languages variable and loop through it down here so basically we're going to map through each item and we're going to get the language and the corresponding version and then we're going to Output a menu item we'll display the language and then right after let's add a space and then display the version so we'll create another text element and give it some Styles make sure to close that out and we'll just add the version right next to it let's also change this color to gray. 600 and now let's save and see how it looks in our app we'll take the language selector import it in our code editor and now see how it looks like in the browser so here are all our options and the corresponding version now let's hook up this menu to a piece of state so when we select a language our code editor here will update to reflect the language that was selected to do that let's go back to our code editor component and right below the value let's add another piece of state and we'll call it language and by default let's just set it to to JavaScript since that's the first item in our list now this code editor component will have the language State and we're going to pass it down to our language selector so let's pass this language as a prop so when an item is selected from this component we want to update this language state so let's declare a function here called on select and it's going to take in a language and then we're going to call set language and just pass in that language now let's pass this on select function into our selector component and wire up this component to this piece of State up here so we'll take the language and on select so let's destructure the language and the on select function and instead of hardcoding JavaScript here let's just output the active language that's in the state and then on our menu item here when we click it we want to run the on select function so let's add an onclick Handler and that's just going to call a function and pass in the language so now that we're updating the actual language State let's go back to our code editor and hook up the actual editor component to the active language that's selected so let's get rid of default language and just pass in language and the value will be the language that's in our state so now if we hit save as we change the language we should be able to write in the code editor with whatever language is selected Let's test that out right now it's JavaScript so let's just declare a variable and that seems to be working and then if I change it to python then this is no longer valid and I'll have to do something like this and we see that we're getting this nice indent and it looks like our syntax highlighting is working the next thing I want to add is when we select a new language it should update this starter code so that we get a little preview of the language and we could easily edit it so right now it's Python and when I change it to C we get this spoiler plate code so in order to do that we need to create a constant back in our constants DJs and we'll just call it code Snippets and basically each language that we've defined here will also exist on the code Snippets and the value will just be some starter code so I just wrote this out on my own and it's basically just boilerplate code for each language so let's take this code Snippets object and go back to our code editor and when we select the new language we set the language State and then right after we want to set the value of our code editor and the value will be that code snippet so we'll do set value and in here let's access our code Snippets object and then pass in the language that was selected so now when this object when the value resolves it'll be one of these strings so let's hit save and test it out so we're in Python now if I go to typescript we get that snippet and then if I go to C we'll get that one and they all seem to be working now let's finish styling this menu because right now we don't know which of these items is selected we have to look up here and let's also change the color of all of this so let's go back to our language selector and we'll start by setting some margin on this box so a margin left of two units and a margin bottom of four units on the menu let's also add the is lazy prop so that all of these children aren't rendered until the menu is actually open and then let's add a background color to the menu list and I'll just set it to a color I picked out earlier and now let's work on the colors of each individual menu item so now that we have the active language being passed in we can know whether the current item we're iterating through is the active language or not to do that let's add a few props so for the color prop we want to check is the language we're currently iterating over the same as the active language so let's actually rename this language to just be Lang so we can differentiate between the current language we're iterating and the language in our state and now we could do a check is the Lang that we're iterating over equal to the language in our state if it is then we want to set the color to blue. 400 otherwise we could just leave it as an empty string next let's add the background color so we want to do the same check so is the language that we're iterating over equal to the language in the state then we'll set the background color to gray. 700 otherwise we want it to be transparent next let's add the Hub state so we'll do underscore hover and then pass it a few Styles so on Hover we want the color to be the active color which is blue. 400 right here and the background to be gray. 900 and since we're using this blue. 400 as our active color I'm just going to store it in a constant above so it's easier to change later on so we'll do const active color and that'll be blue. 400 and then let's replace these two instances so now if we ever want to change the color we only have to change it up here the background color here should actually be gray. 900 not7 and now let's save this and test it out in our browser so I'll just refresh and we have JavaScript selected and then if we select python we'll see that be highlighted and it looks like it's working properly I forgot to add paren to the version so let's go ahead and and add that in real quick in this version let's just wrap it in Parn and then back in the code editor let's get rid of this default value here and let's set it equal to the code Snippets object with the active language selected so now the default value will match the current language now let's just retest this and make sure nothing broke you see that's being displayed properly and it looks like it's working as expected so so that wraps it up for the code editor portion now let's work on this output box so this side of the screen is going to be responsible for taking the contents of the editor sending it to the API and just displaying the output result of that so let's start by splitting the screen into two halves and creating this output box back in our code let's go to the code editor and right above this language selector compon component let's create an H stack and this is a horizontal stack we'll set the spacing to four inside the stack We'll add a box and we'll set the width to 50% and now let's move our language selector and our editor components to live inside this box with a width of 50% and if we save this we should see that it now only takes up 50% % of the screen and there we go so now we have all of this space to add our output box under components let's create a new file called output. jsx and in here we're going to return a box and we'll also set the width to 50% then we'll add a text component we'll say output for its children and then let's add a few props so we'll set the margin bottom to two and the font size large below the text element let's add a button component and inside the button we'll just say run code and then add a few style props we'll set the variant to outline the color scheme to Green we'll give it a margin bottom of four units and below this button let's create our actual output box so we'll do another box here and pass some more props we'll set the height to 75 VH so it'll match what our code editor height is we'll give it a padding of two units a border of one pixel solid we'll set the Border radius to four and then the Border color for now will be 333 then inside this box let's just put some test text for now now let's take this component and import it in our code editor component right after this box with a width of 50% we'll output our component here and now let's see how this looks like in the browser so here's our output there's our button and it all looks as it's supposed to now let's begin wiring up the functionality so when I hit this run code button we need to get the contents of this and make a post request to the Piston API let's start by building out the logic for this run code button we'll go back to our output component and create a function for this button we'll come right above here and declare an arrow function we'll call it run code it's not going to take any arguments and we also need it to be a sync now the first thing we want to do in this function is get the source code in this editor and we know that the value for the code editor is in this code editor component it's in this state called value so we can either pass this state into our output component or just pass the editor ref so I'm just going to pass the editor ref pass it in like that and we also need to know what language is selected so although we have the source code we need to know the language as well so let's also pass the language in the state I'll copy that and right after the editor ref we'll pass in the language so now it's taken these two properties in our output component I'll destructure them here and the first thing we want to do when the button is clicked is get the source code so I'll do const source code is going to be equal to the editor ref. current. getv Val and this is going to return the contents of the editor at this point in time then we'll do a quick check if there's no source code then we just want to return out and not continue on and now we need to make our API call so let's wrap everything with a try catch block since the API call might throw an error we want to make sure we catch it here and now we need to call the API so before we do that we need to create the function that will make the request under Source let's create a file called api.js and I'm going to export a constant called execute code it's going to be async and it's going to take the language and the source code AS parameters now before we can hit the API I'm going to create an API client so let's do const API is going to be equal to axos Doc create now we need to pass in our base URL and that's going to be equal to this URL here this is on the docs that I'll link in the description below and now we can use this client to make post requests so let's go back to our execute code function and get the response so we'll do cons response equals await api. poost and the path to which we post to is SL execute and again that's in their docs we open that up here we go to execute and here's what the request looks like so it's to this path and these are all the properties that we could send in the request body now only a few of these are required and the rest of these are all optional so let's scroll down to the actual example and you can see here is the path and this is the request body so we have to send the language the version a files array with some content and this would be the actual code that you want to execute and then all of these are optional so what's just take this part from the language up to the files and use that in our code so as the second parameter to the post method let's pass an object and paste in those properties for the language instead of JS we want this to be whatever was passed in and the version is going to be the specific version of the language that was passed in and we could get that by opening up the constants and using this object so let's get the language versions paste that in there make sure you import it and then you want to access the current language so this will end up returning one of these strings to figure out which versions the Piston API supports you can go back to the docs and under run times you could make a get request to run times and if you look at the response of that it basically returns an array of all the languages it accepts and then the version that it accepts so I looked up all these languages is beforehand and built out my object here based on the versions they support in a real world app you probably wouldn't want to hardcode them like this because if the API updates then you have to update your code so ideally you'd want to fetch all of these languages beforehand and then use these to build out the menu item here and you'll have all the versions Dynamic to be whatever the API returned so for now we'll just leave it hardcoded let's get back to our post request so now that we have the version being set we don't actually need the file name and for the content instead of this string we want it to be the source code that was passed in so now let's take this response and return the response. DAT because axios wraps this whole API call and on the response object it has a data prop which actually Returns the response body so now let's use this execute code function in our output comp component so we'll go back in here and in our Tri block we want to run that function so let's do a wait execute code make sure to import that we'll pass in the language that is from our state up here and the source code that we got in this line now let's give this a save and pass this run code function to our button click Handler so we'll just go and Define the onclick Handler pass in run code and let's test this out in our browser so we have JavaScript selected and we should see this console log being printed out and I'm also going to open up the network tab so we could see all that traffic now let's hit run code and there's our request going through and if I open it up we'll see that the API responds with this object and there's a language which is the language we sent with the version and then a run property and inside this run property we have the results of our function so we have the output that was printed we get the standard error or the output and now we just basically need to take this response get the Run property and set the output to whatever output the API returned and if there was an error here we want to make sure to also display that to the user so let's destructure this run property and get the output here back back in our code let's destructure run and we'll just rename that to result and now we need to set some sort of output state so in order to do that we need to create above so let's create a piece of state and we'll call it output by default it'll be null and make sure to import use State and right after we get the result let's call set output and pass in the result. output so again result. output is going to be equal to this run. output here now that we're setting the output State let's actually use it to display the output below so let's scroll down and instead of hardcoding test let's do a Turner so if the output exists then we'll display it otherwise we want to display this text here so click run code to see the output I'm just going to copy this over and now let's hit save and we should be able to see our output when it gets set when the API responds so let's give this another test run I'll keep this network tab open and when I click run code it'll run and there we go we see our output now let's try selecting a different language like Java and make sure that that still works and there we go and then we'll just do PHP we'll click run code and there we go so this is all working properly now the last thing we have to do is add proper loading States so we know when the button is clicked the state of the API call and whether an error occurred first let's add our loading state so right below our output we'll create another piece of State we'll call it is loading by default it'll be set to false and when this function runs right before we start this try catch block we want to set the state to loading so we'll do set is loading true and it's going to try to run and and catch any errors and at the end we want to do a finally block and we want to set the is loading state to false so whether it was successful or whether the error was caught at the end of everything we just want to set is loading to false now we need to take this state and pass it to our button here right above the on click let's pass is loading and that's going to be equal to that loading State and now when the button's loading we should see a little loading spinner and that's all done through chalkra UI now let's handle error States so if there was an error with our API call it'll be caught right here in this block and this would refer to if their API is down or it's not working or we just can't reach it so for example if I just comment out one of these properties we should get an error and let's go back and we'll just log the error and we want to display some sort of toast to let the user know that there was an error to do that in Chakra we could go above and Define the toast so we'll do cons toast is equal to use toast and back in our catch block here we can invoke the toast function and pass it some config the title will be an error occurred for the description we want to get the error. message or unable to run code if for some reason there's no message we want to set the status to error and that will make sure that the toast is red and lastly we want to set the duration to 6,000 milliseconds which will just be 6 seconds now if we save we should see the loading and error States so I'll come back here refresh and open up the network Tab and I'm also going to change the network throttling to be fast 3G so we could just see it for a little bit so there's our loading State and then it failed with a 400 and again if we hit it it's loading and we get the error toast you could see here in our a API response we see language is required so that airror handling is working well now let's uncomment that code and make sure everything still works as normal so let's refresh and just test this out again so if I click run code it loads and there we go we'll just select a different language all right so it's all working great now next thing we need to handle is if there's an actual bug in the code for example say I try to use a variable in the code that doesn't exist when I click run code we want to display this error and we do but we also want to let the user know that it was an error so for example we could maybe highlight it as red and set a red border color on this output box to do that we need to create a piece of error State and then apply certain Styles based on that state so let's go back to our output component right below the loading State we'll we'll Define some error State we'll call it is error and by default it'll be false now when this function runs say we reached the API successfully but there was an error during execution in this result we have that result. standard error so we need to check is the result. SCD error defined if it is we're going to set error to True otherwise we're going to set it to false so now that we're setting this state we want to actually use it down in the output box so let's scroll down to our output box and for the Border color instead of hardcoding it to be this let's check is there an error if there is it'll be red. 500 otherwise we want it to be that previous color and then let's set the text color so we'll add the color prop and again we're going to check is there an error if there is we'll set it to red. 400 otherwise it'll it will just be an empty straing so now our output box is hooked up to our error State and we should see it red when there's an execution error let's go ahead and test that out so I'm going to change this name variable to something that doesn't exist click run code and there we see our error State and now if I change it back to name it should go away and there we go I want to add one more Improvement before we finish the video and that's if we have multiple console log statements you'll see that when it gets printed to the output it's just all on one line and in the API response you'll see when we run this code and it has multiple lines they're separated by this back slash and an N so instead of us just rendering out this whole line we should split this string by these characters and then render them one by one on a new line so let's go ahead and add that in real quick back in our code we want to go to our run code function and when we set the output here instead of setting it to the output string we want to call the split method and we want to split on the characters back sln so this is going to return an array of all the segments that were returned after the split so now that our output is an array we can just Loop through it on the bottom and output a new line for each element so in here instead of rendering the output let's render output. Bap and we're going to to get the line and the index of that line and then we'll just return a text element for each line we'll pass the index as the key and then just put the line as its content so now if I save this we should see our output on multiple lines so let's just refresh this I'll duplicate this line click run code and there we go now all the lines are separated so that's it for this video
Info
Channel: Nikita Dev
Views: 9,398
Rating: undefined out of 5
Keywords: javascript, react, programming, react tutorial, javascript tutorial, chakra ui, react app, learn react
Id: THgBePRV13o
Channel Id: undefined
Length: 32min 55sec (1975 seconds)
Published: Thu Feb 01 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.