Professional React v19.0.0 Course
Professional React v19.0.0 Course
React
Development
copyright 2024, Chris Minnick
version 19.0.0,
February 2024
Introduction
• Objectives
• Who am I?
• Who are you?
• Daily Schedule
• Course Schedule and Syllabus
About Me
• Chris Minnick
• From Detroit, currently live in Astoria, OR
• Author of 12+ books, including React JS
Foundations (Wiley, March 2022) and
JavaScript All-In-One For Dummies (Wiley,
May 2023)
• 20+ years experience in full-stack web
development
• Training & coding with React since 2015
Introductions
• What's your name?
• What do you do?
• Where are you?
• JavaScript level (beginner,
intermediate, advanced)?
• What do you want to know at
the end of this course?
• What do you do for fun?
Some of the Topics Covered in this Course
Modern
React.js
JavaScript
Testing
TypeScript
with Jest
Error Best
Handling Practices
React Advanced
Router Topics
Course Repo and More Examples
• github.com/chrisminnick/professional-reactjs
• Here you'll find all the files you need to complete the labs, as well
as the completed solutions for each lab.
• https://reactjsfoundations.com
• This is the website for ReactJS Foundations, by Chris Minnick (Wiley,
March, 2022). It contains all the example code from the book, and
more.
Daily Schedule (CDT)
• 9:00 - 10:30
• 15 minute break
• 11:45 - 12:00
• 1 hour lunch break
• 1:00 - 2:00
• 15 minute break
• 2:15 - 3:15
• 15 minute break
• 3:30 - 6:00 : labs and independent study
7
The Plan
• Instructions: Professional-React-v19.0.0-labs.pdf
• Time to complete labs vary between 15 mins and 1 hour+.
• Let me know if something doesn't work.
• If you finish a lab early:
• try lab challenges,
• help other students,
• do additional reading,
• take a break.
Component-based
Start
CreateReactApp
• Create a React
App
• Test and Run a
React App with
CRA
Most Web Apps today are
built using a set of tools
that enable professional,
team development.
React
with a
The tools, collectively, are
Toolchain known as the JavaScript
development toolchain.
What is Vite?
18
• Objectives
• Understand React
Introduction to
• Explore the Virtual DOM
ReactJS
• Create React Components
• Manage state
A JavaScript library for
What is building user interfaces
React?
React Philosophy
Thinking in Composition
Components vs. Inheritance
Declarative
Imperative getElementById(‘header’)
.innerHTML = “Welcome to my App”;
vs.
Declarative Declarative
Screen
Updates render (
return <header>
Welcome to my App
</header>;
);
React is just
JavaScript
React
JavaScript understandable
to anyone who
knows JavaScript.
If you know
JavaScript, you
can quickly start
writing React.
Lab 02:
Your First
Component
Implements Web Components using
JavaScript
Batches changes
Virtual DOM is a local and simplified copy of the HTML
DOM
vs. HTML • This makes it more efficient than direct DOM manipulation.
• Three Methods
• With Element Variables
• With &&
• With the conditional operator
Conditional Rendering with
Element Variables
function Welcome({loggedIn}) {
let header;
if (loggedIn) {
header = <Header />;
} else {
header = <Login />;
}
return ( <div> {header} </div> ) }
export default Welcome;
Conditional Rendering with the
&& Logical Operator
function Welcome({loggedIn}){
return (
<div> {loggedIn&&<Header />}
Note: if you don't see the
welcome messsage, you're not
logged in. </div>
)
}
export default Welcome;
Conditional Rendering with the
Conditional Operator
function Welcome({loggedIn}){
return(
<div>
{loggedIn?<Header />:<Login />}
</div>
)
}
export default Welcome;
React.Fragment
• Class Child:
class Hello extends React.Component {
render() {
return (<h1>Hello, {this.props.name}</h1>);
}
}
• Function Child:
function Hello(props){
return (<h1>Hello, {props.name}</h1>);
}
55
Step 1: Break The UI Into Step 2: Build A Static
A Component Hierarchy Version in React
Process State
if (a===5) { if (a===5) {
var a = 4; let a = 4;
var b = 1; let b = 1;
} }
console.log(a); //4 console.log(a); //5
console.log(b); //1 console.log(b); //10
Arrow Functions
• OLD
function f (x, y, z) {
if (y === undefined)
y = 0;
if (z === undefined)
z = 13;
return x + y + z;
};
• NEW
function myFunc (x, y = 0, z = 13) {
return x + y + z;
}
Rest Parameter
• http://jsbin.com/pisupa/edit?js,console
Spread Operator
• Spreading of elements of an iterable collection (like an array
or a string) into both literal elements and individual function
parameters.
let params = [ "hello", true, 7 ];
let other = [ 1, 2, ...params ];
console.log(other); // [1, 2, "hello", true, 7]
console.log(MyFunc(1, 2, ...params));
• http://jsbin.com/guxika/edit?js,console
Template Literals
• String Interpolation
• http://jsbin.com/pusako/edit?js,console
Enhanced Object Properties
• Property Shorthand
• Shorter syntax for properties with the same name and
value
• NEW
obj = { x,y };
• OLD
obj = { x: x, y: y};
Method notation in
object property definitions
const shoppingCart =
{
itemsInCart: [],
addToCart (id) {
this.itemsInCart.push(id);
},
};
Array Matching
const list = [ 1, 2, 3 ];
let [ a, , b ] = list; // a = 1 , b = 3
[ b, a ] = [ a, b ];
• http://jsbin.com/yafage/edit?js,console
Object Matching
• http://jsbin.com/kuvizu/edit?js,console
Class Definition
class Square {
constructor (height, width) {
this.height = height;
this.width = width;
}
}
Class Expressions
• Can be unnamed
const Square = class {
constructor(height,width) {
this.height = height;
this.width = width;
}
};
• Or named
class Square {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Class Inheritance
class Rectangle extends Shape {
constructor (id, x, y, width, height) {
super (id, x, y);
this.width = width;
this.height = height;
}
}
class Circle extends Shape {
constructor (id, x, y, radius) {
super (id, x, y);
this.radius = radius;
}
}
Private Methods
• Methods of an object can be private to that object.
• Private methods can only be used within the object.
• To define private methods in classes or objects, preface
the name of the object with #.
class Customer {
#getFullName() {
return `${this.fname} ${this.lname}`
}
greetCustomer() {
return `Welcome, ${#getFullName()}`;
}
}
Static methods
• Implicit Binding
• Explicit Binding
• New Binding
• Window Binding
What is this?
author.logName();
Explicit Binding
const author = {
name: 'Chris',
homeTown: 'Detroit"
}
logName.call(author);
Explicit Binding with .call
logName = function(food1,food2,food3) {
console.log(this.fname + this.lname);
console.log(food1, food2,
food3);
};
let author = {
fname: "Chris",
lname: "Minnick"
};
logName.apply(author, favoriteFoods);
Explicit Binding with .bind
• Works the same as .call, but returns new function
rather than immediately invoking the function
let logName = function(food) {
console.log(this.fname +" " + this.lname +
"\'s Favorite Food was " + food);
};
const person = {
fname: "George",
lname: "Washington"
};
let logMe = logName.bind(person, "tacos");
logMe();
• http://jsbin.com/xikuzog/edit?js,console
new Binding
• Array.map()
• Creates a new array with the results of calling a provided
function on every element in this array.
• Syntax
• const newarray = arr.map(arrayElement => arrayElement + 1);
• Parameters passed to the callback
• currentValue
• The current element being processed
• index
• The index (number) of the current element
• array
• The array map was called upon
Array.filter()
• Array.filter()
• Creates a new array with the results of a test.
• Syntax
• let new_array = arr.filter(test)
• Example
const customersNamedBill =
customerNames.filter(name=>name === "Bill");
Array.reduce()
• Array.reduce()
• Executes a reducer function on each element of an
array.
• Syntax
let value =
arr.reduce((previous,current)=>previous +
current,initValue);
• Example
let orderTotal = items.reduce((total,item) =>
total + item.price,0);
References, Shallow, and Deep
Copies
> ['red','green','blue','orange']
Shallow Copy an Array
104
CommonJS
• export an anonymous function
• Modularity for JavaScript hello.js
outside of the browser module.exports = function () {
console.log("hello!");
• Node.js is a CommonJS }
module implementation app.js
• uses require to include let hello = require("./hello.js");
modules • export a named function
hello.js
exports.hello = function () {
console.log("hello!");
}
app.js
let hello = require("./hello.js").hello;
105
ES Modules
• Named export
• lib.js
• 2 Types export function square(x) {
• named exports return x * x;
• multiple per module }
• default exports • main.js
• 1 per module import {square} from 'lib';
• Default export
• myFunc.js
export default function() {
…
};
• main.js
import myFunc from ‘./myFunc';
107
• Objectives
• Understand how
the DOM works
• Select DOM nodes
• Manipulate the
Object Model
What is the DOM?
• JavaScript API for HTML
documents
• Represents elements as
a tree structure
• Objects in the tree can
be addressed and
manipulated using
methods.
110
EventTarget
111
DOM Events
<script>
getElementById("favoriteMovie")
.innerHTML = "The Godfather";
</script>
114
Manipulating HTML with the DOM
(cont.)
<ol id="favoriteSongs">
<li class="song"></li>
<li class="song"></li>
<li class="song"></li>
</ol>
<script>
let mySongs=document
.querySelectorAll("#favoriteSongs .song");
mySongs[0].innerHTML = "My New Favorite Song";
</script>
115
Manipulating HTML with JQuery
<ol id="favoriteSongs">
<li class="song"></li>
<li class="song"></li>
<li class="song"></li>
</ol>
<script>
$("#favoriteSongs .song").first()
.html("My New Favorite Song");
</script>
116
Manipulating HTML with React
function ThingsILike() {
return (
<FavoriteSongs songList = {[”song1”,”song2”,”song3”]) />
);
}
function FavoriteSongs(props) {
return (
<ol>
{props.songList.map(song => <li>{song}</li>)}
</ol>
);
}
117
Lab 5: DOM
Manipulation
and Modern
JavaScript
• Objectives
• Understanding
components
and elements
• Understand
how to write
components
• Understand
component
life-cycle
• Use events and
event handlers
• Communicate
React Components between
components
What are
components?
• React components
define React
Elements
• Elements are
composed to
create UI
Creating Components
• Two techniques
• Extend React.Component
• Function Component
React.Component
{props.warningMessage}
</p> );
}
Style Objects
• Assign a style object to a variable to make it more reusable.
function WarningMessage(props){
const textColors = {
warning:{color:"red",
padding:"6px",
backgroundColor:"#000000"}
};
return (
<p style={textColors.warning}>
{props.warningMessage}
</p> );
}
Style Modules
• Export style objects to create a library of reusable styles.
<BorderBox>
<p>The first paragraph.</p>
<p>The second paragraph.</p>
</BorderBox>
function BorderBox(props){
return(<div style={{border:"1px solid black"}}>
{props.children}
</div>);
}
Lab 07: Props
and Containers
State
React
Development
Process Step 1: Break The UI Into
A Component Hierarchy
Step 2: Build A Static
Version in React
Step 3:
Identify The Minimal
(but complete) Step 3: Identify The
Representation Of UI Minimal (but complete)
Representation Of UI
Step 4: Identify Where
Your State Should Live
State State
• A JavaScript object used to hold INTERNAL data that influences the output
of the render method.
How State Affects render()
• The state object can only be changed using the
setState() method or a setter function returned by
the useState() hook. This method modifies the
state object and then causes the component to
render.
How to Know if it Should Be State
• Is it state?
• Is it passed from the parent via props?
• Probably not state
• Does it change over time?
• Might be state
• Can you compute it based on any other state or props in your
application?
• Probably not state
Setting Initial State
• In Class Components:
class MyComponent extends React.Component {
constructor() {
this.state = { /* some initial state */ }
}
• In Function Components:
• useState Hook returns a stateful variable and a function for setting it.
• Argument passed to useState is the initial value of the variable.
function MyComponent(){
const [myState, setmyState] = useState("hi!");
}
• setState()
in Class
• Takes an object and
Components merges the object
into the state
object.
Updating
State • setter function
returned by useState
in Function
Components • replaces current
value with
argument
setState
• Function for updating a class component's state outside of the constructor.
• Takes an object or a function as its argument and uses the argument to
schedule an update to the state.
• setState is asynchronous
setState with an Object
• The object will be merged with the current state.
setState with a Function
• The function receives the state and props of the component and returns an
object.
• The object will be merged with the current state.
• Use a function when the new state depends on the current state.
useState setter function
• The 2nd element of the array returned by the useState hook
is a function for updating the state variable (the first
element returned by the hook).
• Unlike setState in class components, the updater function
returned by useState does not merge the new state, it
replaces the old state with the new state.
Valid arguments
• You can pass any value or a function to a setter function.
• If the new state depends on the old state, pass a function to the setter
function.
What to Put in State
• Properties of a component that can change over time.
What Not to Put in State
• Anything that can be calculated.
• Anything passed down from a parent component.
React
Development
Process Step 1: Break The UI Into
A Component Hierarchy
Step 2: Build A Static
Version in React
Step 4:
Identify Where Your
State Should Live Step 3: Identify The
Minimal (but complete) Step 4: Identify Where
Representation Of UI Your State Should Live
State
Step 5:
Add Inverse Data
Flow Step 3: Identify The
Minimal (but complete) Step 4: Identify Where
Representation Of UI Your State Should Live
State
render() {
return <input type="text" />;
}
handleSubmit(e){
e.preventDefault();
// do something here
}
Complex Forms and Validation
• Two methods:
1. Write your own
• benefits
• write only the validators you need
• more customizable
• no need to upgrade third-party component
• disadvantages
• may not be as robust or efficient as a library
• will take longer
• requires writing regular expressions
2. Use a library
• react-hook-form
• formik
• redux-form
Basic Form
Validation
• Demo
Refs
function TextReader(props) {
return (
<textarea ref={textView}
value={props.bookText} />
);
}
render(){
return (
<textarea style={{width:'380px',height:'400px'}}
ref={this.textView}
value={this.props.bookText} />
);
}
}
• Categories
• Mount
• Updating
• Unmounting
• Error handling
Life-Cycle Methods
• Methods of components that allow you to hook into views when specific
conditions happen.
189
Mount/Unmount
191
Updating Lifecycle Methods
• static getDerivedStateFromProps
• shouldComponentUpdate
• returns a Boolean, which determines whether updating
will happen.
• getSnapshotBeforeUpdate
• use for capturing information about the state before
updating
• render
• componentDidUpdate
Error Handling Methods
• getDerivedStateFromError
• Runs when an error happens in a descendant
component.
• Receives the error that occurred and can return an
object for updating the state.
• componentDidCatch
• Runs after a descendant component throws an error.
• Useful for logging errors.
What is an error boundary?
componentDidCatch(error, errorInfo) {
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children; }
}
ReactDOM.unmountComponentAtNode
ReactDOM.unmountComponentAtNode
(document.getElementById('container'));
Component Life Cycle
197
AJAX and Browser
Storage
Promises
What Are Promises?
readFileAsync('myfile.txt')
.then(console.log, console.error);
Async / Await
• Simplifies using promises.
async function f() {
return 1;
}
• An async function always returns a promise.
f().then(alert);
• You can use the await keyword inside an async function to wait until the
promise resolves.
async function getUser() {
let resp = await fetch('http://url.com/users/');
return resp;
}
205
AJAX
• Fetch vs. Axios
• AJAX in class components
• AJAX in function components
• Four Ways
• Root Component
• Best for small apps and prototypes.
• Container Components
• Create a container component for every presentational component that
needs data from the server.
• Redux Thunk
• Lab: Thunk
• Redux Saga
• Suspense & Async Rendering
• Demo: Authentication with React and JWT
AJAX in Class Components
componentDidMount() {
axios.get(`http://www.reddit.com/r/${this.
props.subreddit}.json`)
.then(res => { const posts =
res.data.data.children.map(obj =>
obj.data);
this.setState({ posts });
});
}
AJAX in Function Components
• Simple request
• GET or POST
• Non-simple request:
• Content-Type other than application/x-ww-form-
urlencoded, multipart/form-data, or text-plain or a
request with cookies.
• Server returns Access-Control-Allow-Origin header,
which is checked by the browser.
Access-Control-Allow-Origin
• Hooks!
What are Hooks?
• useContext
• useReducer
• useCallback
• useMemo
• useRef
• useImperativeHandle
• useLayoutEffect
• useDebugValue
Custom Hooks
Component.propTypes = {
name: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
style: PropTypes.object
};
propTypes on Function
Components
import PropTypes from 'prop-types';
function MyComponent(props){
return(
...
);
}
MyComponent.propTypes = {
name: PropTypes.string.isRequired
}
export default MyComponent;
defaultProps
JS TS
function greet(name) { function greet(name:string) {
return `Hello, ${name}!`; return `Hello, ${name}!`;
} }
greet(17); greet(17);
Interface Person {
name: string
}
list.push(item1);
list.push(item2);
return list;
}
• https://github.com/typescript-
cheatsheets/react#reacttypescript-cheatsheets
• Really handy copy / paste examples
File Naming
Pure
Functions Easy to reuse
slice() splice()
let toppings = let toppings =
['cheese','pepperoni','mushrooms']; ['cheese','pepperoni','mushrooms'];
toppings.slice(0,2); toppings.splice(0,2);
// ["cheese", "pepperoni"] // ["cheese", "pepperoni"]
toppings.slice(0,2); toppings.splice(0,2);
// ["cheese", "pepperoni"] // ["mushrooms"]
toppings.slice(0,2); toppings.splice(0,2);
// ["cheese", "pepperoni"] // []
• Always returns the same result given the same • Not Pure!
arguments
• Doesn't depend on the state of the application
• Doesn't modify variables outside its scope
• It's a Pure Function!
React.PureComponent
double(10) // returns 20
Higher Order Components
const EnhancedComponent =
higherOrderComponent(WrappedComponent);
Reusable Components
281
The TDD Cycle
• Red
• write a little test that doesn't work.
• Green
• make the test work, as quickly as possible.
• don't worry about doing it right.
• Refactor
• eliminate duplication created in making the test work.
282
TDD Steps
• Write a test
• Check that test fails
• Write code
• Run test - passes!
• Refactor
• Repeat
283
Red
284
Green
285
Refactor
• Make it right.
• Remove duplication.
• Improve the test.
• Repeat.
• Add ideas or things that aren't immediately needed
to a todo list.
286
Assertions
287
JavaScript Testing Frameworks
• Jasmine
• Mocha
• doesn't include its own assertion library
• QUnit
• from JQuery
• js-test-driver
• YUI Test
• Sinon.JS
• Jest
288
JS Exception Handling
function hello(name) {
return "Hello, " + name;
}
let result = hello("World");
let expected = "Hello, World!";
try {
if (result !== expected) throw new Error
("Expected " + expected + " but got " +
result);
} catch (err) {
console.log(err);
}
289
Jest Overview
Objectives
• Write test suites
• Create specs
• Set expectations
• Use matchers
290
How Jest Works
expect(a).toBe(true);
});
});
291
Test Suites
describe("Hello", function() {
...
}
292
Specs
• Created using the it or test function
• they're the same thing
• Contains one or more expectations
• expectations === assertions
describe("Hello", function() {
293
Expectations
• AKA assertions
• Made using expect function,
• Takes a value
• Chained to a Matcher
• Takes the expected value
expect(actual).toEqual(expected);
294
Matchers
• expect(fn).toThrow(e);
• expect(instance).toBe(instance);
• expect(mixed).toBeDefined();
• expect(mixed).toBeFalsy();
• expect(number).toBeGreaterThan(number);
• expect(number).toBeLessThan(number);
• expect(mixed).toBeNull();
• expect(mixed).toBeTruthy();
• expect(mixed).toBeUndefined();
• expect(array).toContain(member);
• expect(string).toContain(substring);
• expect(mixed).toEqual(mixed);
• expect(mixed).toMatch(pattern);
295
• Objectives
• Learn about
different
rendering
modes
• Learn about
Jest
Testing React • Write Unit
Tests with
Components Jest
What to Test in a React
Component
• Does it render?
• Does it render correctly?
• Test every possible state / condition
• Test the events
• Test the edge cases
Jest
1. Renders a component
2. Creates a "snapshot file" on first run
3. Compares subsequent runs with first and fails test
if different.
Sample Snapshot Test
• An implementation of Flux
• Stores state of the app in an object tree in a single
store
• The state tree can only be changed by emitting an
action
• Specify how the actions transform the state tree
using pure reducers
Stores & Immutable State Tree
• Workflow
• Write top-level reducer to handle a particular function.
• Break up the top-level reducer into a master reducer
that calls smaller reducers to separate concerns.
Higher Order Reducer
let orders = [
{
id:1,
customer:{id:20,name:"Wilma",address:""},
items:[{id:10,name:"Toaster",price:20}
}
]
• Data normalization:
• Each type of data gets its own "table" in the state.
• Each "data table" should store the individual items in
an object, with the IDs of the items as keys and the
items themselves as the values.
• Any references to individual items should be done by
storing the item's ID.
• Arrays of IDs should be used to indicate ordering.
Normalized Redux Store Example
{
orders : {
byId : {
"order1" : {id : "order1", customer : "customer1",
items: ["item1","item2"]},
"order2" : {id : "order2", customer : "customer1",
items: ["item1","item6"]}
},
allIds : ["order1", "order2"] },
customers : {
byId : {
"customer1" : { ... }
...
}
Benefits of Normalizing Store
• Logging actions
• Reporting errors
• Dispatching new actions
• Asynchronous requests
Redux Thunk
root.render((
<BrowserRouter>
<App/>
</BrowserRouter>
))
• Development build
• Uncompressed.
• Displays additional warnings that are helpful for
debugging.
• Contains helpers not necessary in production
• ~669kb
• Production build
• Compressed.
• Contains additional performance optimizations.
• ~147kb
Code Splitting
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
Building Your Project
if (window.hasRestoredState) {
hydrate(<MyPage />, renderTarget);
} else {
render(<MyPage />, renderTarget);
}
Advanced Topics
GraphQL
What is GraphQL
a query language for APIs
a runtime for fulfilling queries
How does GraphQL Work?
Query using JSON-like syntax
Data returned matches shape of query
Example Query and Response
{ {
customer { ”customer”: {
”firstname”: “June”,
firstname
“lastname”:”Sommerville”,
lastname
“address”:”861 Oak St”,
address “city”:”Sacramento”,
city “state”:”California”,
state “zip”:”95814”
}
zip
}
}
}
GraphQL is a pattern
Can be implemented using any language
Tools are available for working with JavaScript, Go,
PHP, Java, C#, Python, Swift, Rust, Ruby, and more.
GraphQL and JavaScript
Server • Client
• Apollo Server • Apollo Client
• Express GraphQL • AWS Amplify
• Relay
GraphQL Pros and Cons
• Pros • Cons
• Even more declarative • Requires GraphQL
• No custom getter logic server
• Tight server integration • More complexity
• Less flexible
Micro Frontends
• Challenges:
• Overhead: Each micro frontend needs to access the
same libraries, CSS, routes
• Can create massive problem of duplication of code and poor
performance
• Cross-cutting concerns: Each frontend needs to share
authentication, for example.
• Communication between micro frontends is more
complex than between components in an app.
Common strategies for React
Micro frontends
• Use a container application to handle rendering
and routing between the micro frontends.
• Use a CDN to host shared frontend libraries and
resources.
• Make use of React Portals to render components
from an application outside of its root DOM node.
Shared State in React Micro
Frontends
• Four strategies
• Web workers
• Props and callbacks
• Custom events
• Pub Sub library
Lab 19:
Creating Micro
Frontends
Thank You
[email protected]
https://chrisminnick.com
Disclaimer and Copyright
Disclaimer
• WatzThis? takes care to ensure the accuracy and quality of this courseware and related courseware files. We cannot guarantee the
accuracy of these materials. The courseware and related files are provided without any warranty whatsoever, including but not
limited to implied warranties of merchantability or fitness for a particular purpose. Use of screenshots, product names and icons in
the courseware are for editorial purposes only. No such use should be construed to imply sponsorship or endorsement of the
courseware, nor any affiliation of such entity with WatzThis?.
Third-Party Information
• This courseware contains links to third-party web sites that are not under our control and we are not responsible for the content of
any linked sites. If you access a third-party web site mentioned in this courseware, then you do so at your own risk. We provide
these links only as a convenience, and the inclusion of the link does not imply that we endorse or accept responsibility for the
content on those third-party web sites. Information in this courseware may change without notice and does not represent a
commitment on the part of the authors and publishers.
Copyright
• Copyright 2021, WatzThis?. All Rights reserved. Screenshots used for illustrative purposes are the property of the software
proprietor. This publication, or any part thereof, may not be reproduced or transmitted in any form by any means, electronic or
mechanical, including photocopying, recording, storage in an information retrieval system, or otherwise, without express written
permission of WatzThis, 567 Commercial St, Astoria, OR 97103. www.watzthis.com