This folder contains 3 demo programs used in sections for the
Autumn Quarter 2019 version of UW's CSE 331.

The demos, 1-* through 3-*, are part of a single React project sitting in this
directory. Each demo is completely contained within a subdirectory of src/. The
only non-directory file in src/ is src/index.js, which selects a demo and makes
it the top-level component in the project.

All projects have a Buggy component, and a Fixed component. We're going to import
them as "App" (just to pick a simple name, there's nothing special about the word
"App") into index.js by changing the import line. That way, we can switch
which demo we're viewing.

>> To launch the React demos:
    - Have a modern version of node/npm installed. (nodejs.org)
    - Run `npm install` in the same directory as this README.
    - Run `npm start` in the same directory as this README.
    - Change the import in index.js to select the desired demo.
    - The import can be changed while the project is already running to quickly
    switch between demos.

An index of each demo is listed below:

 =====================
 ===> 1-lifecycle <===
 =====================

Demonstrates what can happen why you try to access data before a component
has been fully initialized. In this case, it seems like the `canvas` object
inside `this.canvasRef.current` is null!

>> To Run:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './1-lifecycle/Buggy';`

The bug comes because we're trying to access it during the constructor. Specifically,
we're accessing this.canvasRef.current inside updateCanvasImage, which is called
during the constructor. According to the react lifecycle diagram (see the bottom
of this README for a link), the constructor is called by React before render() is
called. Since render() hasn't been called, we never gave the <canvas> element to
React. Therefore, it hasn't been put into the page yet, so there isn't a Canvas
object yet.

Now that we know what the bug is, we have an idea of how to fix it. We need to wait
to access the canvas until after we know that the <canvas> element has been inserted
into the page. Luckily, React has a lifecycle method for that: componentDidMount.
We can override that method with whatever code we want, and React will call it when
it's done creating and rendering our component. Moving the updateCanvasImage call
to that method means that we'll wait until we know for sure that the canvas exists
before we try to access it.

>> To Run the Fixed Version:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './1-lifecycle/Fixed';`

 =================
 ===> 2-state <===
 =================

This is a simple page with a piece of text and a button. The button is supposed to
modify a boolean flag that indicates whether it's been clicked. If the button is
clicked, the text on the page should indicate that change. Right now, the text on
the page never changes, regardless of how many times you click the button. We know
that the button click is being detected successfully, because the console.log in
the button handler is working - so why is the text not changing?

>> To Run:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './2-state/Buggy';`

The problem comes because `this.clicked` - our boolean flag - is just an instance
variable inside our component. It's being changed just fine, you could add logging
to prove it if you like. However, remember that you're only deciding what text to
display inside the render() function. If React called render(), for example, 30
times per second, then when you pushed the button the boolean would change, and
the next time render() runs, you would correctly return the changed text and
your component would seem to work fine.

This would be very slow, because React would constantly be calling render() even
if there's nothing new to show. To solve this, React has a special instance variable
called `state`. You create an object and assign it to state (using {...} syntax
for objects in JS), and then you can put whatever fields you'd like in that object
to keep track of the "state" of your component: like whether the button has been
clicked.

Let's change the `clicked` variable to be stored inside the `this.state` object.
Wherever we used `this.clicked`, we'll write `this.state.clicked`, and in the
constructor we initialize `this.state` instead of `this.clicked`.

>> To Run a Second Attempt:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './2-state/Buggy2';`

Now we're storing the data in state, like we should but it still isn't working! We
also still know that the button clicks are working fine, because the console.log
still works. What's wrong?

The problem here is because React has no way of knowing that the state has changed!
Sure, it could, for example, check the contents of the `this.state` variable 60
times a second and see if things have changed, but then we have the same performance
problem as before! To solve this, React has a rule that you should never modify
the `this.state` variable directly. In fact, if you have the React Developer Tools
extension for Chrome (see below for a link), you'll even get a warning about this
in the console.

How does React allow you to modify state then? You call `this.setState()`. setState()
is a method that you inherit from the Component class, and it does a number of things.
The two things we care about right now are:
 - Allows you to request changes to the state.
 - Tells React that the state might be different, and to update accordingly.

Therefore, to fix our code, all we need to do is change the direct assignment
of this.state into a call to setState(). By requesting that React make the state
change for us, we know that React will notice that the state is changing. If you
refer back to the lifecycle diagram from earlier, you'll notice that calling setState()
will (eventually) cause a component update cycle to happen, which ends with React
calling render() again. This means that React will call render() once it's done updating
the state, which is exactly what we want! Now, when render() is called again, we'll
notice that the boolean flag has changed and we can return the elements with the
updated text.

>> To Run the Fixed Version:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './2-state/Fixed';`

 ==================
 ===> 3-desync <===
 ==================

Here, we have a slightly more complex example. There's a canvas on the page that starts
out blank. There are three buttons that save a color into the a state variable. We
want the color in the state variable to affect what color shows up in the canvas.

>> To Run:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './3-desync/Buggy';`

Notice that the behavior of this is odd: it's almost doing the right thing, but the
canvas seems to be lagging behind the rest of the page. What's wrong? We know that
the color is being updated properly in state because the text being displayed in the
<p> element is always correct. However, our canvas is always one color behind, showing
whatever color we selected two clicks ago instead of the most recent click.

Take a look at the code and see how the canvas is re-drawn: we make calls to drawSquare
from within each of the setXYZ functions, right after the setState call. The best way
to explain what's going on here is to realize that setState() actually not a great
name, it'd be much more accurate to call it makeRequestForFutureStateChange().
The reason for this is that setState() doesn't change the `this.state` field right
away, it just tells React that you'd like for it to make the state update when it's
able to. This means that, if you were to try to access state right after (like we're
doing here), it probably hasn't been updated yet.

The reason React is lazy about updating state is for performance. In reality, it'll
update the state so fast that no human would be able to notice the delay. However,
giving React control of when to update the state allows it to make some optimizations
that make applications with lots of state updates run WAY faster.

The problem here is that we're trying to access state immediately after the setState
call (effectively 2 lines of code execution after), so it's likely that React hasn't
had a chance to update the state during the time it took those 2 lines to execute.
Fortunately, our trusty lifecycle diagram is here to save the day! We know that
setState calls ultimately cause a component update cycle. It turns out that the
component update cycle happens after React has update the actual `this.state` variable.
Just like in the buggy example 1, there's a method we can override to be notified
when a component lifecycle event has happened. For updates, it's componentDidUpdate.

The fix, then, is to remove our drawSquare() calls from each of the button click
handlers. Instead of trying to access state right away, we want to wait until React
has finished updating it for us. We'll know that happens when we're inside the
componentDidUpdate function - so we move our drawSquare() call there.

Now, when the button is pressed, the following happens:
 - The button calls its onClick handler, which is one of the setXYZ functions.
 - That function calls setState to ask React to update the state.
 - Some time later (a few milliseconds), React will finish updating the this.state variable.
 - React calls render, which returns the updated <p> element describing which color is
 now selected.
 - React calls componentDidUpdate, which calls drawSquare, which reads the updated
 state and modifies the canvas accordingly.

By waiting to read the state until after we know that it's up-to-date, we ensured
that we weren't responding to stale, old data and kept our application in sync.

>> To Run the Fixed Version:
    - Follow the "launching React demos" instructions above, if you haven't already.
    - Change line 3 of index.js to `import App from './3-desync/Fixed';`

Note: the fixed version of this code also calls drawSquare() from inside
componentDidMount. This just makes sure that when the component is first created the
canvas has something in it instead of being left blank. This isn't necessary for the
state updates to work properly.

 ====================
 ===> Extra Info <===
 ====================

React Lifecycle Diagram:
    http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

React Developer Tools Chrome Extension:
    https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en