You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
15 KiB

import { addBanner, addArticle, addTitle, addHeader, addParagraph, addClassParagraph, addSubHeader, addOrderedList, addUnorderedList, addBlockquote, addInset, addInsetList, addInsetCodeListing, addInsetBulletList, addImageWithCaption, addButtonGroup, addSidebar, addSyntax, menu, global_menu } from '/scripts/import.js';
import { local_menu } from '/scripts/webdev.js';
const heading = document.querySelector(".heading");
const global = document.querySelector(".global_menu");
const local = document.querySelector(".local_menu");
const sidebar = document.querySelector(".sidebar");
const main = document.querySelector(".main_content");
heading.append(addTitle("Learning ReactJS"));
heading.append(addParagraph("Eve Porcello - LinkedIn Learning"));
heading.append(addParagraph("Chapter 5 - React Enhancements"));
main.append(addSubHeader("Updating with the useEffect Dependency Array"));
main.append(addParagraph("We saw in the precious exercise that useEffect works with other stateful hooks such as useState and useReducer (which we will see later). Whenever the state changes, React re-renders the component tree and we saw that useEffect is called after the re-render. Now. we will look into useEffect in a bit more depth."));
main.append(addParagraph("We are going to get rid of our CheckBox() function and put our App() function back. The App() function is going to render two labels displaying \"Favourite Phrase\" and \"Second Favourite Phrase\" along with an input box for each label."));
main.append(addParagraph("We will use useState to manage the state of the input boxes so we will need two constants with appropriate values and set value functions and we will use empty strings as the initial state."));
main.append(addParagraph("We will tie these to the input boxes in two ways. First, we will set their value to match the values stored in useState and second, we will give them an onChange function so that when the value is updated, that value is passed to the set value function. So the App() function will look like this."));
main.append(addImageWithCaption("./images/figure78.png", "Figure 78 - the App() func with labels for Favourite Phrase and Second Favourite Phrase (and input boxes)"));
main.append(addParagraph("The syntax of the onChange function looks a little strange in comparison to the previous example. It looks like we are passing an argument, e, to the setVal (or setVal2) function and setting its target value."));
main.append(addParagraph("Next, we are going to add a useEffect which will log to the console, the value of the input boxes. For the first box, the useEffect will look like this"));
main.append(addInsetCodeListing(["useEffect(() => {", " console.log(`field 1: ${val}`);", "}"]));
main.append(addParagraph("The useEffect for the second box will be similar, but we will log this as field2 and change val to val2."));
main.append(addParagraph("If we go back and refresh our app and then start typing in the input boxes, we can see that this is being logged to the console. For instance, let's say we type 'something' into input box 1 and 'else' into input box 2, we will see the output shown in figure 79."));
main.append(addImageWithCaption("./images/image29.png", "Figure 79 - the output from our app showing the state of the input boxes being logged to the console"));
main.append(addParagraph("Notice that as we type 'something' into input box 1, we are seeing each change to the value being logged but we are also seeing field 2 being logged, in spite of the fact that we are not changing anything in input box 2. Similarly, we can see that when we do start typing into input box 2, we are seeing the value of input box 1 being logged each time."));
main.append(addParagraph("Each time there is a change in either input box, the values of both input boxes are being logged. In most cases, you would only want to log changes to the value that is being changed."));
main.append(addParagraph("To do that, we are going to send a second argument to use effect. Remember that the console.log operation is the first. The second argument that we send to useEffect is called the dependency array and this will hold the state variable that we want to listen for changes in. So, the call to useEffect is amended as follows."));
main.append(addInsetCodeListing(["useEffect(() => {", " console.log(`field 1: ${val}`);", "}), [val];"]));
main.append(addParagraph("Again, we will make a similar change to the second call, replacing val in the dependency array with val2."));
main.append(addParagraph("We will clear out the values in these input boxes and refresh the app. We will then type the same values ('something' in input box 1 and 'else' in input box 2). This time, we see the results shown in figure 80."));
main.append(addImageWithCaption("./images/image30.png", "Figure 80 - the app showing more or less the same console output as we saw in figure 79 but streamlined by using a dependency array"));
main.append(addParagraph("Of course, the dependency array is an array, not a single value. So, we can still use either call to useEffect to listen for changes in both input boxes simply by passing both values to useEffect in the array. For example, if we wanted to do that, we could call useEffect like this."));
main.append(addInsetCodeListing(["useEffect(() => {", " console.log(`field 1: ${val}`);", "}), [val, val2];"]));
main.append(addParagraph("So, the dependency array can be quite useful in helping to avoid unnecessary re-renders by passing the right values to that array. As such, it is a really important part of React hooks."));
main.append(addSubHeader("Fetching Data with useEffect"));
main.append(addParagraph("Another use for the useEddect hook is to fetch data. As an example, if we go to <a href=\"https://api.github.com/users/\">https://api.github.com/users/</a>, we can see a list of GitHub users and we can also append a / and a user name to retrieve data for a specific user such as <a href=\"https://api.github.com/users/PhilipOsztromok\">https://api.github.com/users/PhilipOsztromok</a> or <a href=\"https://api.github.com/users/EvePorcello\">https://api.github.com/users/EvePorcello</a>. This data is displayed as JSON data in the web browser."));
main.append(addParagraph("What we want to do here is to incorporate this data into a GitHub user in our own app and we will start with an empty App() function. We will still render our &#60;div&#62; with App(), but we will also user it to render another component called GitHubUser which is going to fetch the data from the API."));
main.append(addParagraph("Let's take a look at the GitHubUser() function first."));
main.append(addImageWithCaption("./images/figure81.png", "Figure 81 - the GitHubUser() function"));
main.append(addParagraph("To start with, we are using useState and array destructuring so that we are returning a value for data and a function called setData to update it."));
main.append(addParagraph("We are then using useEffect to fetch data and we are providing it with a partial URL. The login comes from the GitHubUser() function (it is passed to the function by the App() function). the data is returned and is then used with the setData function to set the data for out GitHubUser component."));
main.append(addParagraph("The if statements checks whether any data was returned and if so, it uses a JSX template to take the returned JSON data and convert it to a &#60;div&#62; containing text data - a string, in other words."));
main.append(addParagraph("In no data is returned, our &#60;div&#62; still needs to return something so we are returning null."));
main.append(addParagraph("The App() function returns a GitHubUser component which has been initialised with the provided username so the App() function looks like this."));
main.append(addImageWithCaption("./images/figure82.png", "Figure 82 - the App() function (returning s GitHubUser component)"));
main.append(addImageWithCaption("./images/image31.png", "Figure 83 - a browser displaying the GitHubUser component (in the form of stringified JSON data)"));
main.append(addParagraph("Rather than display this blob of stringified JSON data, we could also display the user login instead and we can also access other data from the JSON such as the user's avatar. This would mean returning a &#60;div&#62; that looks like this."));
main.append(addParagraph("Note that we are getting the login and the URL for the user's avatar from the data that useEffect fetched for us and we are also giving the &#60;img&#62; element a width property so that it displays nicely in the browser."));
main.append(addParagraph("This is dynamic so we can change the user's login name in the App() function in order to fetch a new set of data. For instance, I don't have an image associated with my GitHub account so all I see is my name and a standard block image."));
main.append(addParagraph("If I change the login name to MoonHighway, we see a login name and a user avatar."));
main.append(addImageWithCaption("./images/image32.png", "Figure 84 - the browser displaying the username and avatar from a GitHubUser component"));
main.append(addParagraph("There are actually several different ways to fetch data with React but this is a common pattern."));
main.append(addSubHeader("Using useReducer"));
main.append(addParagraph("As we have seen, useState manages states in our applications and useEffect handles side effect, that is, anything that is not related to the UI. Another React hook is useReducer and to look at this, we are going to go back to our CheckBox() function as shown below."));
main.append(addImageWithCaption("./images/figure85.png", "Figure 85 - our CheckBox() function again"));
main.append(addParagraph("A quick recap, this displays a check box and a bit of text telling us whether it is checked or not and it uses useState to manage that state."));
main.append(addParagraph("This is fairly straightforward, but it does add unnecessary complexity in that we have a toggle on line 10 and this is being passed to the setChecked function every time it is called and this is how we are changing the state of the check box."));
main.append(addParagraph("We could abstract this out into its own function which would look like this."));
main.append(addInsetCodeListing(["function toggle() {", " setChecked(checked =&gt; !checked);", "}"]));
main.append(addParagraph("What we will do, is to replace useState with useReducer. At its most basic, a reducer function is one that takes in the current state of a component and returns a new state."));
main.append(addParagraph("So, we will pass the toggle function as the first argument to useReducer."));
main.append(addInsetCodeListing(["const [checked, toggle] = useReducer(", " checked =&gt; !checked,", " false"]));
main.append(addParagraph("The second argument, false, is the initial state and notice that we still use checked as the state variable. In this case, toggle is simply the name that the function is using for the change."));
main.append(addParagraph("The main benefit of useReducer is that it can help manage the complexity in your applications."));
main.append(addParagraph("For an introduction to hooks, see the documentation at <a href=\"https://reactjs.org/docs/hooks-intro.html\">https://reactjs.org/docs/hooks-intro.html</a> and for more information on specific hooks, including the three that we have looked at here, see the Hooks API Reference at <a href=\"https://reactjs.org/docs/hooks-reference.html\">https://reactjs.org/docs/hooks-reference.html</a>."));
main.append(addSubHeader("Deploying a React App"));
main.append(addParagraph("There are a number of ways to deploy a React app, but create-react-app does simplify this process. Let's take a quick look at the package.json file which is in our project folder."));
main.append(addImageWithCaption("./images/figure86.png", "Figure 86 - the package.json file"));
main.append(addParagraph("Notice that we have a build script, this is shown on line 16 in figure 86, and we can use this to create a production build for the app. We do that with the command"));
main.append(addSyntax("npm run build"));
main.append(addImageWithCaption("./images/figure87.png", "Figure 87 - shell output showing part of the build process"));
main.append(addParagraph("On line 2, I have used the ll command (which is an alias for ls -l) to list the contents of the directory. The command to build the project is on line 11 and the following lines (13 to 36) are the output of the build command."));
main.append(addParagraph("On line 38, we have a second ll command which shows the contents of the directory after the build is complete and you can see that a new folder, build, has been created."));
main.append(addParagraph("One option to then serve up the app is with a static server so we would run the command"));
main.append(addSyntax("serve -s build"));
main.append(addParagraph("The output is somewhat familiar and it gives us a URL with an assigned port number, 5000 in this case so we can view our app with the URL (in my case) of"));
main.append(addSyntax("http://192.168.0.13:5000/"));
main.append(addParagraph("You should also be able to access it via"));
main.append(addSyntax("localhost:5000"));
main.append(addParagraph("Bear in mind that if, like me, you are writing the code on a remote computer (in my case, a Raspberry Pi which I am accessing via ssh), then localhost won't work because the localhost is the remote machine."));
main.append(addParagraph("It will work on the remote machine, of course, so I can access the Raspberry Pi desktop via RDP and use this URL in a web browser on the Pi or I could do this directly on the Pi."));
main.append(addParagraph("It may also be worth noting that serve may not be installed. It was for me, but if necessary, we can use npm to install it."));
main.append(addSyntax("npm install -g serve"));
main.append(addParagraph("Note, the -g option here is for global. It looks like this command will also update serve if it is already installed so maybe worth running it, even if it is already there."));
main.append(addParagraph("There are several options for deploying the app to the web and the one covered in the course video is <a href=\"https://netlify.com/\">Netlify</a>. This requires a sign-in but you can sign in with your GitHub account which may be handy if you are deploying a project you have developed on GitHub."));
main.append(addParagraph("You can also deploy your app by simply dragging and dropping the builder folder into the area with the dashed border in Netlify (this can pose a bit of a problem when using a remote computer so I have had to upload the folder to my nextcloud account and download it from there on to ny PC."));
main.append(addParagraph("This happens very quickly, although we should bear in mind that this is just a very small sample app created for learning purposes, but Netlify provides a URL, in my case this is <a href=\"https://agitated-swanson-7d22d5.netlify.app\">https://agitated-swanson-7d22d5.netlify.app</a> from which the app can now be accessed."));
main.append(addParagraph("So, using Netlify, our app has been successfully deployed to the web!\""));
main.append(addParagraph("There are other similar services you can use such as Google Cloud, Heroku or Azure and you could, of course, self-host which ultimately is what I would want to do."));
3 months ago
addSidebar("webdev");