React
Review
- Questions about “raw” DOM creation?
createElementinnerTextaddChild- https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ReactJSXIntro/clientSideDomCreation.js
- Use of
React.createElementto 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
propsobject has just one key:ingredients - Notice how we use the sub-component
Ingredientwith an HTML-tag-like syntax. - Notice how we pass parameters using
key=valuesyntax.- 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 InstructionsRecipeMenu- 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
useStatesolves 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.
selecteddetermines the coloronStarClickedis a function passed down from above- When a star is clicked, then that function is called
StarRating.jsxtotalStars(how many iterations to run the loop)selectedStars(the rating)onRatingUpdated(function to call when the rating changes)- Explain how
selectedStarsgenerates the visual of the rating - Explain how clicking a specific star causes
onRatingUpdatedto be called with the new rating as a parameter. - Highlight the difference between
onClick={foo}inStar.jsxandonClick={() => 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
useStatehere)
Color.jsxtitleprop- color is displayed as a
divwith a givenbackgroundColor StarRatingis the list of stars (i.e., it contains the individualStarcomponents)- A
colorobject contains several properties (title, color, rating, etc).titleandbackgroundColorare used directly here.ratingis 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
updateRatingfunction in theAppcomponent.ApptoColorList: “Call this function if the rating changes.”ColorListtoColor“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.
updateRatingcallssetColorson 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.useStatedefined 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
useStatehook 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
sqlite3andExpress: https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqliteCallbacks/db/SqliteToyDB.js- Notice that the
allmethod 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 (
showin https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqliteCallbacks/controllers/ToyController.js)- Notice that the calls to
renderas 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.lastIDrequires the use offunctionvs.() => - 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
toysBelowandtoysAboveare 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
nextmethod 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
setTimeoutcall 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.questionis asynchronously fetching keyboard input.)
- Now back to the DB: https://github.com/kurmasz-SampleCode/CIS371-SampleCode/blob/master/ExpressMVCSqlitePromises/db/SqliteToyDB.js
allToyson 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.
nextcan take a parameter. Parameters passed toresolvebecome the parameters tonext.- Similar for
rejectandcatch. - show how
finduses bothresolveandrejectandshowusescatch- 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
thenandcatch.
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
fetchColorsmethod. fetchis a built in JavaScript function.- It returns a
Promise. - When the “fetched” data is ready, the callback inside of
thenis invoked. - The data returned from the server is the
responseparameter. - 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
fetchcallback 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
useEffecthook 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
useStatehooks loadingkeeps track of whether we are in the process of loading data.messagekeeps track of the message that will be displayed to the user indicating what is going on.
- We add two
useEffectruns the specified block of code after initial render.- First it sets
loadingto true, which re-renders the page with a “loading…” message. - When data is finally ready
message“turned off”loadingset 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
reloadCountreloads the data from the server. - If I replace with an empty array,
useEffectnever 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).
useMemowill create a consistent “hash” value when necessary.