For more information, please see our print and e-book bulk sales website at http://www.apress.com/bulk-sales. Any source code or other supplemental material referenced by the author in this book is available to readers on GitHub through the book's product page located at www.apress.com.
About the Author
Introduction
The checkpoints and links to the differences are listed on the home page (README) of the repository. All commands to be executed on the console will be in the form of a block of code starting with .
Hello World
It actually gets turned into JavaScript code that generates an element in React's virtual DOM. The other two scripts, react.js and react-dom.js, are the core React libraries that handle the creation and rendering of react components.
EXERCISE: JSX
Windows users can follow nvm for Windows (search for it in your favorite search engine), or install Node.js directly without nvm. If you are using Windows and installing Node.js directly, make sure you also have a compatible version of npm installed.
EXERCISE: NPM
Let's put it all together in a file called server.js in the root directory of the project. In the case of Express, the module executes a function that can be used to instantiate an application.
EXERCISE: EXPRESS
Let's name this external script App.jsx and place it in the static directory so that it can be referenced from the browser as /App.jsx. We also need to modify index.html to replace the reference to App.jsx with App.js and indicate the new type of this script; it is now JavaScript and not JSX.
EXERCISE: BABEL
But instead of installing individual polyfills, let's use the recommended babel polyfill that emulates a complete ES2015 environment. Let's discuss the new code, which includes some ES2015 features and also a React feature.
EXERCISE: ES2015
The curly braces allow you to insert JavaScript expressions inside the JSX and these will be replaced by the value of the expression. In fact, all the responses to the prompts when we ran npm init were recorded in package.json.
React Components
We'll hardcode the data used to display the page, leaving persistence and data retrieval to a later chapter. To begin, we will replace the Hello World example with a simple class as a starting point for the Issue Tracker application.
EXERCISE: REACT CLASSES
In most cases, the value of the attribute is a string that looks the same as the HTML attribute value. Since we are at an early stage in the development of the application, we expect several changes to the properties.
EXERCISE: PASSING DATA
We left the end date undefined in the first object to indicate that this is an optional field. In the IssueTable class, we also need to expand the header row for the table to include all the fields.
EXERCISE: DYNAMIC COMPOSITION
If you have a component that needs to be passed, you can use a child component if it is deeply nested and naturally occurs in the child component. Another choice for the key property is the array index because it is also unique.
React State
Initializing the state is as simple as setting the this.state variable to the state object. Finally, we called this.setState with the new array, thus changing the state of the component.
EXERCISE: SETTING STATE
We also get rid of multiple bindings by replacing this.createTestIssue with a permanently bound version in the constructor. The click handler is called handleSubmit, and inside this method we read the form's input values and using them, we call the createIssue function, which is available to the component via this.props.
EXERCISE: COMMUNICATE CHILD TO PARENT
Anything that can change as a result of an event somewhere in the component hierarchy qualifies as part of the state. Since there is no way to communicate between siblings (only between parents and children and vice versa), keeping the state at the base of the hierarchy is the best strategy.
Express REST APIs
REST
PATCH can be used to modify a resource by adding it to an array in the resource. With a multiple call, the resource is constantly changing because the same element is added to the array over and over again.
JSON
Route parameters are named segments in the route specification that match a part of the URL. If a match occurs, the value in that part of the URL is returned as a variable in the request object.
EXERCISE: THE LIST API
We got the new case to be added from the request body (which was already parsed by the body parser). The last statement in the holder is responsible for returning the new case in response to the request.
EXERCISE: THE CREATE API
It takes the path of the URL to retrieve and returns a promise with the response as the value. We need to parse the response, for which we can use the json() method of the response itself.
EXERCISE: USING THE LIST API
So we used the concat() function for Array, which creates a copy of the original array and is therefore safe. Of course, restarting the server will reset the problem list to the original.
EXERCISE: USING THE CREATE API
We need to check the response's property response.ok, and if it's not OK, we need to throw an error. If you didn't care about the response message, you could have just thrown an error in the first step, where you just pass the status text message instead of the application error message.
EXERCISE: ERROR HANDLING
Since JSON only allows primitive types, dates are encoded as an ISO 8601 string in the JSON. Testing with a malformed JSON prints an error and stack trace to the server's console.
Using MongoDB
As in a relational database, you can use a primary key and indexes for the collection. The updateMany() and updateOne() variants were introduced in version 3.2 to make explicit the purpose of the update.
EXERCISE: MONGO SHELL
Add some more statements to a script file, the same ones you wrote in the mongo shell for inserting and reading documents. Let's create a mongo shell script called init.mongo.js and place it in a new scripts directory in the project directory.
EXERCISE: SCHEMA INITIALIZATION
The first two elements in the array are the node and the program name. For all other function calls, we passed the callback to the MongoDB driver method.
EXERCISE: READING FROM MONGODB
Refresh the browser and the two issues that we initialized using the mongo shell script should appear. Testing this set of changes will show that new issues can be added, and even on a restart of the Node.js server or database server, the newly added issues are still there.
EXERCISE: WRITING TO MONGODB
If the database is unavailable for a short period (30 seconds), the driver waits and reconnects when the database is available again. If you'd rather throw an error if the database isn't available, set bufferMaxEntries to 0 when you connect.
Modularization and Webpack
Finally, we need to change the script file referenced in index.html to the new app.bundle.js instead of App.js. There will be some remaining temporary files, App.js and IssueAdd.js, in the static folder that you no longer need, so they can be removed.
EXERCISE: TRANSFORM AND BUNDLE
The webpack-dev server looks to a special section called devServer in webpack.config.js for its parameters. If you make a change in any of the client-side code, you'll also see the webpack-dev server rebuild the bundles.
EXERCISE: HOT MODULE REPLACEMENT
But we need some changes in webpack.config.js which will not affect the production configuration but will allow us to apply the changes. This can be seen in the initial output of webpack-dev-server (you may need to browse on your console), or when changes are made to client-side code.
EXERCISE: SERVER-SIDE ES2015
Let's create this file in the project directory and initialize it by expanding the Airbnb ruleset. The first type of exception is to override the rule itself, by setting various options for the rule in the .eslintrc configuration file.
EXERCISE: ESLINT
This can lead to confusion during development if you don't keep an eye on the server compilation console. The static compilation method is faster than the required hook, especially when there are incremental changes to the server code.
Routing with React Router
React Router works by taking control of the main component that is rendered in the DOM. Let's also include the issue ID in the placeholder page to make sure we can access the parameters in the IssueEdit component.
EXERCISE: ROUTE PARAMETERS
Compared to that, the change in the query string is just a change in one of the properties, which does not warrant reassembly of the component. We used the componentDidMount() lifecycle method to hook into the initial ready state of the component.
EXERCISE: ROUTE QUERY STRING
The second method is by injecting the router property into the components that need it, using the React Router withRouter method. We had to bind the method to this in the constructor because the method needs access to this.props.router.
EXERCISE: PROGRAMMATIC NAVIGATION
To configure this, all we need to do is set the routes in the router configuration, with paths relative to the parent route. We will look at this technique in the next chapter when we complete the problem editing page.
EXERCISE: MORE FILTERS IN THE LIST API
In this event, you need to set the state variable based on the user's input. If they are different, it sets the value of the input back to the state variable.
EXERCISE: FILTER FORM
At this point, React compares the input value in the virtual DOM with the state variable. The code is quite similar to the Create API, except for parsing and checking the input parameter for the problem ID.
EXERCISE: EDIT PAGE
Note that we used {..this.props} after the type property but before the other properties as value. If this.props contained value, onBlur, or onChange properties, they would not be passed to .