React
Review
- Questions about “raw” DOM creation?
createElement
innerText
addChild
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ReactJSXIntro/clientSideDomCreation.js
- Use of
React.createElement
to simplify the above process.- Combines element creation, addition of attributes/style, and the addition of children.
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ReactJSXIntro/complexDOMcreateElement.js
- Use of JSX as a “shortcut” to writing calls to
React.createElement
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ReactJSXIntro/jsx1.jsx
- In general, a block of JSX should produce a DOM element (i.e., the results of running
React.createElement
)
- Can insert JavaScript into JSX using
{}
.
New Stuff
- Show video slide with components
- We want to display a list of recipes.
- We want to avoid duplicating code.
- Notice we are repeating the same pattern with different data.
- So, this should be analogous to calling a function with different parameters.
- Each recipe is further broken into sub-components (like helper functions)
IngredientList
- A component can be used elsewhere almost as if it were an HTML tag:
<IngredientList>
- To make it abstract, we pass parameters.
- Well, one parameter to be specific, called “props”.
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ReactRecipe/recipe1.jsx
- Look at the data: An array of nested JavaScript objects (i.e., key-value pairs)
- Look at “innermost” component:
Ingredient
- Notice that an object describing an ingredient has three properties:
name
,amount
, andmeasurement
- We can refer to those inside the JSX.
- Notice that an object describing an ingredient has three properties:
- Look at data structure for
ingredients
: Just an array. - Now look at
IngredientListDemo
- This time the
props
object has just one key:ingredients
- Notice how we use the sub-component
Ingredient
with an HTML-tag-like syntax. - Notice how we pass parameters using
key=value
syntax.- These key/values are passed as a single parameter (i.e.
props
)
- These key/values are passed as a single parameter (i.e.
- This time the
- Now look at the “real”
IngredientList
Instructions
Recipe
Menu
- Insert into DOM
Javascript short-cuts
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/JavaScriptBasics/deconstructingArrays.js
- Deconstructing arrays (
useState
) - Array spread operator
- Spread out
- re-group
- Deconstructing objects (
props
) - show constructing an object, then compare it to the “old” way:
let person = { fname, lname, occupation }
console.log("Notice that variable names become key names: ");
console.log(person);
let person2 = {
fname: fname,
lname: lname,
occupation: occupation
}
- Does this redundancy look familiar?
- Use
...
on objects to clone objects- And add properties when cloning.
- Look at
recipe3.jsx
State
stateUsingClosures.js
: https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ReactRecipe/stateUsingClosures.jsrecipe2.jsx
- The component functions are responsible for rendering (drawing) the components.
- We can’t store state inside these functions, because the state would get re-set every time a component was re-rendered.
- We don’t want to simply throw state into a global variable (for all the reasons that global variables can be problematic).
- The closure-based
useState
solves this issue:- State is stored outside the function; but,
- access to that state is still limited to the scope of the function.
- Also, the hook does more than simply store state. The inner workings also look at state changes to optimize re-rendering.
ColorList
- Demonstrate
ColorList1_fetch
- Note: This is covered in the book. Probably more effective to read at your own pace than try to learn everything from me in “real time”. I’ll try to point out the confusing parts here.
Star.jsx
- Shape is imported from a library.
selected
determines the coloronStarClicked
is a function passed down from above- When a star is clicked, then that function is called
StarRating.jsx
totalStars
(how many iterations to run the loop)selectedStars
(the rating)onRatingUpdated
(function to call when the rating changes)- Explain how
selectedStars
generates the visual of the rating - Explain how clicking a specific star causes
onRatingUpdated
to be called with the new rating as a parameter. - Highlight the difference between
onClick={foo}
inStar.jsx
andonClick={() => foo()}
here. (Emphasize the use of the closure to keep track ofi
.) - Notice that we need to store the rating at a higher level, so it is passed down to this level (rather than there being a
useState
here)
Color.jsx
title
prop- color is displayed as a
div
with a givenbackgroundColor
StarRating
is the list of stars (i.e., it contains the individualStar
components)- A
color
object contains several properties (title, color, rating, etc).title
andbackgroundColor
are used directly here.rating
is passed down to theStarRating
.
ColorList.jsx
- Just iterates through an array of colors.
App.js
:- For now, just the form and the list of colors.
- Notice that this component is the “source of truth” for the color data.
- The color data must be stored here (and is “pushed down” into the components)
- Any changes must be passed “up” to this level.
- When a star is clicked, rather than changing a local state, it needs to call a function in the
“upper-level” component letting it know what happened. To make this happen, each upper-level component
passes a function as a prop. The lower-level component calls this function to report activity/state change. Thus, when a star is clicked, it starts a chain of function calls all the way back up into the
updateRating
function in theApp
component.App
toColorList
: “Call this function if the rating changes.”ColorList
toColor
“Call this function if the rating changes.”- …
- Notice that this is not the same function all the way down (although it can be).
- Important Notice that when the list of colors changes, a new list is created.
updateRating
callssetColors
on a new list of colors. It does not simply modify the existing list.- If you just modify the existing list, then the React engine may not detect the change and perform the update.
Forms
NewColorForm.jsx
- Controlled vs. uncontrolled components.
- Notice the use of local state and also callback to higher level.
API
- Switch to building a Rails API so colors don’t have to be “hard coded”.
18 March
Review:
- Page organized as a hierarchy.
- Data passed down through the hierarchy as “props”.
- State saved at the highest level of the hierarchy were it is needed using
useState
.useState
defined inside a component (but not in the return block.- It is not defined in the global scope.
- To change state, pass a function down through the props.
- That function is invoked “below” where the handler is — and usually passed parameters.
- The function is defined in the same scope as the
useState
hook so that it can modify the state. - Modifying state causes components to re-render.
- To modify state, objects must be replaced, not modified.
Callbacks vs. Promises
- JavaScript is asynchronous by nature and, therefore, uses a lot of callbacks.
- Think about handlers and timers.
- Callbacks can lead to code that is difficult to read and organize
- I’ll show an example in a minute.
- A better way to handle asynchronous code is
Promises
Callbacks
sqlite3
andExpress
: https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqliteCallbacks/db/SqliteToyDB.js- Notice that the
all
method call sends both (a) an SQL statement to the SQLite engine and (b) a callback — code that is executed when the request is complete. - The list of rows is passed as a parameter to the callback, it is not the function’s return value!
- Notice that the
- Also look at
find
,create
, andupdate
. - Using the DB elsewhere in the code requires a callback (
show
in https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqliteCallbacks/controllers/ToyController.js)- Notice that the calls to
render
as well as the error handling end up nested inside the call toToyDB.find
- At the moment this is not terrible; but, it does take some getting used to.
- Notice that if the controller had multiple steps, control flow would not be strictly “top to bottom”. It would be “top to bottom, then back to the callback”
- Notice that the calls to
- Notice that the error handling is a bit clunky.
- Notice the potential for SQL injection.
-
The use of
this.lastID
requires the use offunction
vs.() =>
- What is the benefit of this quirky callback system?
- Concurrent behavior
“Callback Hell”
- Look at bottom of https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqliteCallbacks/db/SqliteToyDB.js
toysBelow
andtoysAbove
are simple enough.- Now, what if we want to combine the results of two separate SQL calls (i.e.,
extremes
)- Ignore the fact that this particular example could be a single SQL statement.
- One call must be nested inside the other.
- Logical flow is not “top to bottom” but rather “outside to inside”
- (This is also true of the simpler cases; but, just not quite as obvious.)
- Imagine what adding error handling could do to this code
- What happens when there is a long sequence of callbacks that must be made:
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/JavaScriptAsynchronous/callbackHell.js
- Even writing separate functions doesn’t help much.
Promises
- A class / programming style that replaces “callback hell” with a more sequential-feeling style.
- The basic features can actually be implemented in “user space” — no need for special programming language features.
- Of course, Promises are built into JavaScript.
- (Not sure if there are aspects that do require specific PL, either to exist or for performance reasons.)
- Fundamentally, a Promise is an object that takes an asynchronous callback as a parameter.
- The parameters to the callback are the callbacks to the asynchronous function.
- I know that sentence makes no sense.
- Promise object also has a
next
method that is invoked when the the inner asynchronous call is complete. - https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/JavaScriptAsynchronous/timerPromise.js
- Moves the callback from inside the
setTimeout
call itself, to a subsequent call tonext
. - For one invocation, this is a minor change; but, it allows us to chain callbacks rather than nest them.
- Moves the callback from inside the
- Even more important benefit of Promises is improvement in error handling.
- First, imagine having an if/then error check at every level of the nested callback.
- Now, consider https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/JavaScriptAsynchronous/timerPromiseWithFeedback.mjs
- Specifically look at
askAreYouUp
. (io.question
is asynchronously fetching keyboard input.)
- Now back to the DB: https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqlitePromises/db/SqliteToyDB.js
allToys
on the implementation side is not much different- The call to the DB itself is still asynchronous (that’s the way that library was written).
- We need to pass a callback.
- But, but wrapping up the DB access in a promise, the “consumer” of the DB class has cleaner code: https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqlitePromises/controllers/ToyController.js
- Well, not a huge difference … yet.
next
can take a parameter. Parameters passed toresolve
become the parameters tonext
.- Similar for
reject
andcatch
. - show how
find
uses bothresolve
andreject
andshow
usescatch
- again, not a huge change; but, the error detection is not so ‘ad-hoc’.
- The real power if Promises comes when we either
- Want to run multiple queries in parallel (
all
), or - Need to sequence multiple queries and want clean error handling.
- Want to run multiple queries in parallel (
- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqlitePromises/db/promiseDemo.js
- Promises were a lifesaver when writing code for SpectrumHealth when we needed to pull data from the userDB, the Epic medical records DB, and the Priority Health insurance DB all at the same time.
- You will rarely create your own Promises. It is far more common that you will use libraries that already use Promises.
- So, you will be getting Promise objects as return values from library calls, and
- then using
then
andcatch
.
Using fetch
with Promises to connect to API
- Back to https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ColorList1_fetch/src/App.js
- Look at the
fetchColors
method. fetch
is a built in JavaScript function.- It returns a
Promise
. - When the “fetched” data is ready, the callback inside of
then
is invoked. - The data returned from the server is the
response
parameter. - The callback/Promise system prevents the JavaScript engine from stalling while waiting for the API to respond.
- Look at the
- There is a subtlety here: The
fetch
callback is made when the server begins to return data. - If the amount of data returned is large; we may want to
- wait for the entire response, or
- process chunks of the response as it comes in.
- Therefore, the methods of the response object that provide content also return a
Promise
— a chain! - Calling
response.json()
returns a promise that is resolved when the entire data response is un-marshalled into a JavaScript object.- It is a common beginner mistake to forget this second promise and attempt to use
response.json()
directly.
- It is a common beginner mistake to forget this second promise and attempt to use
Adding API calls to ColorList
- We already saw the code that fetches the data from the server. Now, let’s wire it up.
- The
useEffect
hook is code that runs code in the background.- Notice that we don’t want the page load to stall waiting for the API data.
- Specifically, we don’t want a blank screen or a “spinning wheel” or something like that.
- We want a meaningful page to load quickly, then get updated when the rest of the data arrives.
- To achieve this
- We add two
useState
hooks loading
keeps track of whether we are in the process of loading data.message
keeps track of the message that will be displayed to the user indicating what is going on.
- We add two
useEffect
runs the specified block of code after initial render.- First it sets
loading
to true, which re-renders the page with a “loading…” message. - When data is finally ready
message
“turned off”loading
set tofalse
- Color data updated.
- First it sets
- Show what happens if
- delay changed
- API raises exception
- API shut down.
- IMPORTANT Notice the second parameter to
useEffect
- It specifies dependencies – values that when changed should cause the hook to re-run.
- Notice that changing
reloadCount
reloads the data from the server. - If I replace with an empty array,
useEffect
never re-runs. - If I omit the parameter, then it re-runs if any state changes.
- This is bad because the whole purpose of the hook is to change
colors
- The result is an infinite loop
- This is bad because the whole purpose of the hook is to change
- Take care: Some values will always appear different (e.g., arrays built on the fly).
useMemo
will create a consistent “hash” value when necessary.