heading.append(addTitle("React: Creating and Hosting a Full-Stack Site"));
heading.append(addParagraph("Shaun Wassell - LinkedIn Learning - September 2022"));
heading.append(addParagraph("Chapter 1 - Creating a React Front End"));
main.append(addHeader("SETTING UP A REACT PROJECT"));
main.append(addParagraph("First of all, we should check to confirm that we have node installed with"));
main.append(addSyntax("node -v"));
main.append(addParagraph("and also npm"));
main.append(addSyntax("npm -v"));
main.append(addParagraph("For this course, we will generate our basic React App using a generator which will give us a skeleton project with some boilerplate code."));
main.append(addParagraph("Before running the generator, you will navigate to the folder you want to create the app in and run the command "));
main.append(addParagraph("The process can take a few minutes. When done, you will see that it has created a directory with the project name."));
main.append(addParagraph("As a point of interest, I am developing the project on my Raspberry Pi Dev machine using the developer account and I will be using Visual Studio code. This means that we can look at the files that have been created for us either with a simple ls command in the terminal or we can open the folder in VS Code and inspect the files there."));
main.append(addParagraph("The image below shows the project folder opened in Visual Studio Code with the public and src folders expanded."));
main.append(addImageWithCaption("./images/vscode1.jpg","The project folder opened in Visual Studio Code."));
main.append(addParagraph("Before we get into the files that have been created by the generator, we can test to confirm that the app has been correctly generated. In a terminal, we will navigate to the project folder and run "));
main.append(addSyntax("npm start"));
main.append(addParagraph("or"));
main.append(addSyntax("npm run start"));
main.append(addParagraph("In a browser, we can then go to"));
main.append(addSyntax("127. 0. 0.1:3000"));
main.append(addParagraph("This will start up the default React App as shown in the image below."));
main.append(addParagraph("The generator created the files for the default app which we will edit to create our own app. For the purpose of this course, the app will be a blog."));
main.append(addParagraph("There are a couple of JSON files which describe the structure of the app and its dependencies. The files that we will edit are in the public and src folders."));
main.append(addParagraph("The public folder contains the basic files for the app so, for instance, there is an html file which is the app's launch page. The file is shown below."));
main.append(addParagraph("In the src folder we have all of the support files for the app and this includes css and Javascript files. We also have test files for the component which we won't be covering in this course. Notice that we have a file called App.js and that's going to contain the components for the site and it is referred to as the root component."));
main.append(addHeader("CREATING THE APP COMPONENT"));
main.append(addParagraph("Let's start by taking a look at the root component, App.js."));
main.append(addParagraph("This contains the code for the spinning logo and the link to the React documentation that we saw in the default app. The logo is on line 8 and the link is on line 14. None of this is relevant to our app so we will delete everything inside the div with class Name App."));
main.append(addParagraph("We can now start to create the interface so we will start by adding a couple of elements which will allow us to confirm that the code for the default app is gone and to start adding some styling."));
main.append(addParagraph("We can also delete the statement in App.js that imports the logo and the logo itself from the src folder."));
main.append(addParagraph("In the App div, we will insert an HTML element with the text, My Awesome Blog and a div with id page-body with the text, Welcome to my blog! Later, we will create blog pages and these are going to be inserted into this div."));
main.append(addParagraph("We can run the App again or if it is still running, we can just refresh the page and we will see our own app in the browser."));
main.append(addImageWithCaption("./images/blog1.jpg","The browser displaying the changes we made to App.js."));
main.append(addParagraph("The styling we are getting from App.css is for the default app so we want to change that and we will do that by copying the CSS from the Exercise Files. You might notice that there is a lot of CSS because this is all of the CSS for the app. It includes rules for classes and ids that haven't been added yet!"));
main.append(addHeader("CREATING YOUR BLOG PAGES"));
main.append(addParagraph("The next step is to create the individual pages for the blog. It's important to understand that we won't create an individual page for each blog post, as we will see in a moment, but we do want to create a kind of framework with the pages that we will need for the app, and this includes the page which will be used to display the blog posts."));
main.append(addParagraph("We will create a folder to hold these pages, which we will call pages. Inside that, we will create five files for each of our pages. Initially, these will be empty but we will add to them as we progress through the course. These pages are shown below."));
main.append(addImageWithCaption("./images/pages.jpg","The pages folder showing the pages we have added as a starting point."));
main.append(addParagraph("The filenames are fairly self-explanatory but as it is the most important for our app, I would point out that the Article Page is page is the page where our blog articles will be displayed."));
main.append(addParagraph("We will look at the Not Found js page later. For the time being, we will add some code to the other pages so that we can load them up and know that we are looking at the right page."));
main.append(addParagraph("For each page, we will define a function that will return the appropriate page. To do that, we will define a constant that will hold the contents of that page, an anonymous callback function in which we will generate the contents and an export statement that will allow us to import these contents elsewhere."));
main.append(addParagraph("This is shown in the image below."));
main.append(addImageWithCaption("./images/homepage_js.jpg","The basic code for the home page."));
main.append(addParagraph("We can copy this and paste it into the other 3 pages with the appropriate modifications. This means that we can now navigate to any of these pages and the header will let us know if we are right . In other words, if we want to see the About page, we should see that displayed."));
main.append(addParagraph("For now, we will import these for pages into the app component with statements like"));
main.append(addSyntax("import Homepage from './pages/HomePage'"));
main.append(addParagraph("Navigating to the correct page is a little bit different to how you would do that it you were creating a page with just HTML and CSS. Here, we are using a concept called routing so rather than a location being defined by the site's directory structure, it will be defined relative to the page that imported it."));
main.append(addParagraph("We displayed our App with the URL"));
main.append(addSyntax("localhost:3000"));
main.append(addParagraph("which is to say that we use this URL to display the app's root component. We imported our pages into the root component, so we can display them relative to that. To do that, we will delete the current contents of the page-body div and we will replace it will all four of our pages so the div will now look like this."));
main.append(addParagraph("We can save this and reload the app which will give us the result shown below."));
main.append(addImageWithCaption("./images/all_pages.jpg","The root component displaying all of the imported pages together."));
main.append(addParagraph("Clearly, this isn't what we want. We want to be able to select a route to, for example, the About page and have the component respond by displaying that page ."));
main.append(addParagraph("In order to set up the routing, we have to install a package called react-router-dom which we do with the command"));
main.append(addParagraph("I will do that in the same terminal window the app is running in to avoid accidentally trying to install it in the wrong directory so I will stop the app with control + C and then run the install command."));
main.append(addParagraph("Once it's been installed, we can import the components we will need with the command"));
main.append(addSyntax("import { BrowserRouter, Routes, Route from 'react-router-dom';"));
main.append(addParagraph("To use these, we need to enclose our apps interface, so that's the App div, inside a BrowserRouter element. Inside the page-body div. we have all four of our pages which we will put inside a Routes element. Finally, we will define a Route for each page like this."));
main.append(addParagraph("The revised root component with the routing added is shown below."));
main.append(addImageWithCaption("./images/routing.jpg","The root component with our routing added."));
main.append(addParagraph("A couple of points of interest here. The path attribute is similar to an href attribute you might see in HTML but as we mentioned earlier, it doesn't correspond to a folder in the sites code. It's just a way of saying if the user types this in the address bar, we should route them to this page."));
main.append(addParagraph("The / on its own can be omilted in the address bar and this is the page a user will be routed to if he/she types"));
main.append(addSyntax("localhost:3000"));
main.append(addParagraph("in the address bar or rather the sites URL, which will obviously be different if the site is live on the internet. For the other pages, the path is added to the URL so if we go to"));
main.append(addSyntax("localhost:3000/about"));
main.append(addParagraph("we will be routed to the About page like this."));
main.append(addImageWithCaption("./images/about.jpg","The About page showing that the routing is working correctly."));
main.append(addParagraph("You may have noticed that the route for the ArticlePage is a little different."));
main.append(addParagraph("The articleId is what is known as a URL parameter and we need it here because this page isn't designed to display one specific thing, it needs to be able to display any blog past so we will give each post an id which we can then add to the URL. The URL will then route the viewer to the specific post/article that they are looking for."));
main.append(addParagraph("At this point, we can navgate to a page with some random articleId, for example"));
main.append(addParagraph("you will see the Article Page. Since we added an article id, the server knows to display the ArticlePage rather than the Articles ListPage. However, since there is no article with that id, we don't see anything other than the basic page."));
main.append(addParagraph("At this stage, I would guess that this is essentially key be the not found page which I imagine will be set up so that it displays when the article in is not recognised but we will see that later."));
main.append(addParagraph("So now, we can navigate between the different pages via the URL. The next step is to set up links so a user can navigate between the pages without needing to know what they need to type into the address bar in order to access a particular page."));
main.append(addParagraph("To start with, we will add a nav bar so we will add NavBar.js to the source folder. This is a component which we will add to our pages, but it's not a page itself so it doesn't go in the pages folder."));
main.append(addParagraph("Since the NavBar is a component, we will generate it in the same way we generated the pages so we will have a callback function that returns a NavBar component. The component itself is a semantic nav element containing an unordered List. Each List item will contain a link."));
main.append(addParagraph("For the link, we will import Link from react-router-dom and then we will add each link which will look something like this."));
main.append(addParagraph("To display the NavBar component, we will import it into the App component and replace our header with it. Once we have done that, we can reload the page which will now display the NavBar as shown."));
main.append(addImageWithCaption("./images/navbar.jpg","The App displaying our Nav Bar component."));
main.append(addParagraph("We can now click on the links and we will see the appropriate page being displayed."));
main.append(addHeader("URL PARAMETERS WITH REACT ROUTING"));
main.append(addParagraph("At this point, we have the basic structure of our blog, albeit with very little content at the moment. Next, we want to work on the artilce page so that it displays an actual blog article rather than just a placeholder."));
main.append(addParagraph("We are going to get our articles from the Exercise files and these are contained in the file article-content.js. The top of the file is shown in this image."));
main.append(addParagraph("Notice that it is not using a function to generate the articles. It is simply defining a const array called articles. Each element of the array is one article and the format is json."));
main.append(addParagraph("The name will be our article id so if we import that array into our article page, we can then use the name to access the appropriate article."));
main.append(addParagraph("When the user types in a URL with an article id such as"));
main.append(addParagraph("we will grab the article-id (the URL parameter) and use it to get the right article. In order to do that , we will use a special hook called useParams which we will import from react-router-dom. We will import that in to our article page (ArticlePage.js) and we can then create a variable that will call the useParams function to get the value."));
main.append(addParagraph("This actually returns an array of parameters. We can get the article id in several ways, so if we create a const called articleId, we could set it's value like this, for example."));
main.append(addParagraph("we will see that the article id is shown as learn-react. Notice that at this point, we aren't doing any checking to see if that is a valid article id so we could type anything there and it would be displayed as the article id."));
main.append(addParagraph("That's not an error since we are defining the article id as whatever is tagged onto the end of the URL so if we change the URL to"));
main.append(addParagraph("the id will be shown as notAnArticleId. OF course, it doesn't match any of the ids in our array so it is not an article id but it is the URL parameter!"));
main.append(addParagraph("Remember that the articles are held in an array which is defined in and exported from article content is so if we want to display one of them in ArticlePage.js, we will need to import the array."));
main.append(addSyntax("import articles from './article-content';"));
main.append(addParagraph("We can then set up a const to hold the article and to find the specific article we want it to hold, we will use the find function on the articles array like this."));
main.append(addParagraph("So we are using the article in we got from the URL parameter to find on article in the array where the name value is the same."));
main.append(addParagraph("Assuming that the article id does match one of name values in the array, that article will be held in the const article which means that article holds a JSON object. We can now completely replace the content for Article Page with a header which is set with the title from that JSON content and we can loop through each paragraph in content to generate our paragraph elements."));
main.append(addParagraph("This will look something like the following ."));
main.append(addImageWithCaption("./images/articlepage_js.jpg","The article page configured to display an article if a valid article id is provided."));
main.append(addParagraph("You might notice that there is an empty element tag around the contents we want to return (the opening tag is on line 9 and the closing tag is on line 14 in ArticlePage.js shown above). This is because we need to pass one element back to the app so this is a workaround allowing us to create an element that contains both the header and the paragraphs. This is known as a React fragment."));
main.append(addParagraph("In a sense, this is really just the same as saying that a function can only return one value. If you miss that out, you will see errors in your code (assuming you are using an IDE Such as Visual Studio Code with error highlighting) and you will also see errors in the browser."));
main.append(addParagraph("Assuming everything is correct and the URL contains an appropriate URL parameter, you will see something like the page shown below."));
main.append(addImageWithCaption("./images/learn-react.jpg","The browser displaying the article with the article id, learn -react."));
main.append(addParagraph("What do you think will happen if we now add an invalid article id? For example, let's say we put"));
main.append(addParagraph("into the address bar. In ArticlePage.js file, our function is returning a header and several paragraphs which it gets from the element in the articles array where the title matches our article id. If there is no article with that name, nothing is returned so we have a header with no title (so, empty) and no paragraphs. The result is that we simply see an empty page (actually, there are errors in the background as we will see later)."));
main.append(addParagraph("This isn't ideal it would be better to in some way let the user know that they have typed in an invalid article id and we will look at fixing that later."));
main.append(addHeader("CREATING AND LINKING THE ARTICLE LIST"));
main.append(addParagraph("As things stand, a user can navigate to the different pages in the blog but will still have to know the URL for an individual article. The next thing we want to do is to fix that and as you will probably have guessed, we will use the articles list page for that."));
main.append(addParagraph("We want to have a List of the articles displaying the name and a short sample from the article. In the articles list page, we will start by importing the array of articles because we are going to use it to get the data we want to display."));
main.append(addParagraph("We will amend the header so that it just says \"Articles\" rather than \"This is the articles list page\". Then we will loop through the articles in the same way we looped over the paragraphs in the article page and for each article found in that array, we will display a h3 element with the article title."));
main.append(addParagraph("Note that within the JSON object for each of the articles, the content key has a value that is an array of paragraphs. In this case, we only want to get the first paragraph from that array and that has an index value of 0."));
main.append(addSyntax("article.content[0]"));
main.append(addParagraph("We don't know how big the first paragraph will be so we want to take the first 150 characters, and that is returned by"));
main.append(addSyntax("substring(0, 150)"));
main.append(addParagraph("Finally, we added a few dots at the end to demonstrate to the user that there is more content."));
main.append(addParagraph("In order to allow the user to click on an article in the list and see the complete article, we will use the same Link component we used in our nav bar so we will have to import that from react-router-dom. We can then put the title and summary between Link tags so we will need to import the Link module."));
main.append(addParagraph("Finally, we will add a to property to our Link so that it takes the user to the correct article when the link is clicked. This is pretty similar to the to property in our nav bar so we have a to property. We will use an article id but since we have access to the articles array including the names which will be used as the article ies, we will use the name in the Link."));
main.append(addParagraph("Remember, we are already looping through the array and getting the title and the date for our summary, we can also get the name and use it to generate the to property so the path will look like this."));
main.append(addParagraph("Since we are using a variable name inside the string to set the value of the to property, we need to surround it will back-ticks and the whole thing goes inside a pair of curly braces to give us this."));
main.append(addParagraph("If we go back to the articles list page, we can now click on one of the articles listed there and the appropriate article is loaded."));
main.append(addParagraph("We want to make one additional tweak. You might have noticed that the text in the articles list page is styled like a link. There is some styling to tidy that up so we need to add a class name to the link in order for that styling to be applied and the class is article-list-item. Once we have added that, the page in the browser looks much better."));
main.append(addParagraph("If you look in the console, however, you will see that there are some errors as shown below."));
main.append(addImageWithCaption("./images/errors.jpg","Errors generated by the articles list page."));
main.append(addParagraph("The errors are being caused by the fact that when you use something Like map to generate a list dynamically, React will expect you to provide a unique key for each of the list items."));
main.append(addParagraph("The key property should be added to the outermost element of each list item which for us is the Link element so we will add it like this."));
main.append(addParagraph("If we go back to the browser and refresh the page, these errors will now disappear. You might recall that we also used something similar when writing the code to display on article where the paragraphs for each article are held in an array and that will give us the same error."));
main.append(addParagraph("Unlike the articles, the paragraphs don't have a convenient identifier we can use as the key so one possibility would be to use the actual paragraph as the key so that solution would give us"));
main.append(addParagraph("A better solution would be to use the index value of the paragraph so that would mean getting that value from the array along with the paragraph which means that the first line of the loop will become"));
main.append(addParagraph("So now we can save these changes and refresh the page which should clear those errors."));
main.append(addParagraph("Note that using the index value as a key works best if both the actual array elements and their order stay the same so if you use this technique in your own code and find that you still get key errors, you may need to find another value for the key."));
main.append(addParagraph("For reference, the code for ArticlePage.js is shown below."));
main.append(addImageWithCaption("./images/articlepage_js.jpg","The modified code for ArticlePage.js."));
main.append(addParagraph("The code for ArticleList.js is"));
main.append(addImageWithCaption("./images/articlelist_js.jpg","The code for ArticleList.js."));
main.append(addHeader("MAKING YOUR ARTICLES LIST MODULAR"));
main.append(addParagraph("In it's current state, most of the basic functionality of the blog has been implemented and it works perfectly well for the articles that we have for our articles list. However, we might want to use the same functionality again. For example, we might want to display a similar list showing only the most popular articles on or home page or we might want to display a list of related articles on each of our article pages. It would take some additional coding to make that work but it would be helpful if we could reuse our existing list component."));
main.append(addParagraph("One of the advantages of using React is that it does allow you to make use of reusable components so if you have a component that does something you might find useful in a variety of contexts, you probably want to think about making it reusable."));
main.append(addParagraph("In terms of how to do that, it is fairly straight-forward since we already have that functionality in the ArticleList.js file. If you think about the code in that file, it basically generates our HTML with a header followed by a list of articles with in-built links and the component exports the whole page."));
main.append(addParagraph("What we are aiming for here is to have that List generated by the reusable component which we can than import into ArticleList is so we are really just using a different technique to generate the page."));
main.append(addParagraph("We will start by creating a subfolder in src which we will call components."));
main.append(addParagraph("In the components folder, we will create a file called ArtilesList.js and we can copy the DOM structure from ArticlesListPage.js. We will change the const and return variables to ArticlesList."));
main.append(addSyntax("import articles from './article-content';"));
main.append(addParagraph("and paste it into ArticlesList.js. You might notice that, although this works it would only ever export the same list that we had generated previously which means that it's not really reusable."));
main.append(addParagraph("What we want is to be able to tell the component which articles it should be using to generate the list and we can do that by adding a prop to the component so"));
main.append(addParagraph("In ArticlesPagelist.js, we can import the ArticlesList"));
main.append(addSyntax("import ArticlesList from '../components/ArticlesList;"));
main.append(addParagraph("In place of the code to generate the list, we will use the imported Articles List. Essentially at this point we are telling React to import the list so at the same time, we can tell ArticlesList.js what articles to use to generate the list with so this is essentially a function call."));
main.append(addParagraph("so essentially, we are still importing articles into ArticlesListPage.js and then passing it over to ArticlesList.js in order to generate the list. If we want to use a different list elsewhere, such as a list of related articles, we would have to devise some method of determining what constitutes a related article such as adding a subject or other form of categorization to articles-content. In the article page, we could then import those articles, use the subject (or whatever) to select the related articles and then send that list of articles to ArticlesList is to generate our list."));
main.append(addParagraph("If we go back to the browser, we will see that our app works exactly as it did before, but now we have a reusable component. The important point to take from this is that when you are creating an app in React, it is useful to have this mindset where you are thinking about what code you might want to reuse which might be better placed in a reusable component."));
main.append(addParagraph("Again, I want to finish this section with a look at the code in ArticleListPage.js."));
main.append(addImageWithCaption("./images/articlelistpage_js1.jpg","The ArticleListPage.js file making use of a reusable component."));
main.append(addParagraph("We will also take a look at the code for our component, ArticleList.js."));
main.append(addImageWithCaption("./images/articlelist_js.jpg","The code for our reusable component, ArticleList.js."));
main.append(addHeader("CREATING A 404 PAGE IN REACT"));
main.append(addParagraph("Our app is almost complete apart from a couple of things which are the home and about pages don't really have any content yet so we want to put at least some dummy text there so that out blog looks like a real blog."));
main.append(addParagraph("The other thing we need to do is to implement our page not found page and that will also include a scenario where the user tries to open an article with an invalid articleid."));
main.append(addParagraph("For both the home and about pages, we will simply copy these over from github (01_09e)."));
main.append(addParagraph("Remember, we previously created a file called NotFoundPage.js and we then ignored it while we went about creating the other pages so we'll come back to that now."));
main.append(addParagraph("For this file, it's going to just return a page with a header that says 404"));
main.append(addImageWithCaption("./images/notfoundpage_js.jpg"," Implementation of the Not Found page."));
main.append(addParagraph("For a very simple function like this, we can actually dispense with some of the syntax and just essentially assign the element to the const. This is a common technique in React and makes the code a little bit smaller and ensier to read. The modified code looks like this."));
main.append(addImageWithCaption("./images/notfoundpage_js1.jpg","A simpler version of the NotFoundPage.js file."));
main.append(addParagraph("The most obvious difference is that we are not returning anything . In the previous version , the function returned an HTML element whose value was assigned to the const which was then returned. The revised version is essentially skipping that step and simply assigning the const a string value containing the HTML element and returning that."));
main.append(addParagraph("This technique can be used whenever your return statement contains just one statement. We could, for example, use the same technique to generate both the home and about pages."));
main.append(addParagraph("Now that we have a not found page, we want to make sure it is displayed if a user tries to navigate to a page that doesn't exist."));
main.append(addParagraph("You might recall that our App.js file contains routes for the home page, about page, article list and article page and the app also imported these pages. We will add NotFoundPage to the list of imports and add a route to take the user to that page if the page is not found."));
main.append(addParagraph("Now, aside from the not found page, we only have the other four pages I mentioned so you might say that any route that doesn't take you to one of the four pages should take you to the not found page so we will add our route like this."));
main.append(addParagraph("The asterisk is a wildcard to this is essentially saying that any route should take you to the not found page. This is inserted at the end of the list of routes which means that when the user inputs a URL, the app will check to see if it matches one of the routes that will you to one of our apps pages."));
main.append(addParagraph("If it doesn't match any of those four pages, we don't need to check it any further and at that point, we can say that any route is invalid and should take the user to the not found page. For that reason, it is very important to make sure it is the last route to be checked."));
main.append(addParagraph("For the articles, I mentioned that if you use an invalid a article id to view an article, you will see a blank screen because essentially the page is displaying a title and some paragraphs that don't exist but in fact, this does generate an error behind the scenes."));
main.append(addParagraph("If the article you are looking for is not found in the array, it is undefined so when we try to get, for example, the title like this"));
main.append(addParagraph("you will get an error, something along the lines of cannot get title from undefined. To avoid this and ensure the not found page is displayed instead, we will need to check, before we try to display the article, that we actually have a valid article to display."));
main.append(addParagraph("Our if statement (the conditional) will look something like this."));
main.append(addParagraph("If we back to the app and load up an article and then change the article id to something random, the result will be that we see the NotFoundPage."));
main.append(addParagraph("At this point, the front-end of the app is complete so we can now move on to writing the back-end."));