Using a Custom Template
npx create-react-app my-app --template @appacademy/simple
Keep in mind that using create-react-app automatically initializes a git repository for you!
You can also simplify the html file into:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>Simplifying the src folder
Remove: App.css App.test.js logo.svg serviceWorker.js setupTests.js
Update the Following Files:
// ./src/App.js
import React from "react";
function App() {
  return <h1>Hello world!</h1>;
}
export default App;
``;// ./src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);Class Components
// ./src/Message.js
import React from "react";
const Message = (props) => {
  return <div>{props.text}</div>;
};
export default Message;ES2015 Version
// ./src/Message.js
import React from "react";
class Message extends React.Component {
  render() {
    return <div>{this.props.text}</div>;
  }
}
export default Message;class component by using this.props// ./src/index.js
import React from "react";
import ReactDOM from "react-dom";
import Message from "./Message";
ReactDOM.render(
  <React.StrictMode>
    <Message text="Hello world!" />
  </React.StrictMode>,
  document.getElementById("root")
);Setting and accessing props
class Message extends React.Component {
  constructor(props) {
    super(props);
    // TODO Initialize state, etc.
  }
  render() {
    return <div>{this.props.text}</div>;
  }
}super method with props passed through it.React.createclass function, if you ever need to use this antiquated method make sure you install a module called create-react-classStateful components
One of the major reasons why you would choose to use a Class Component over a Function Component is to add and manage local or internal state to your component.
Second of the major reasons is to be able to use a Class Component’s lifecycle methods.
What is state?
internal to the component.
When to use state
State is more often used when creating components that retrieve data from APIs or render forms.
function component.Functional:Stateless || Class:Stateful
Initializing state
this.state object.// Application Entry Point
// ./src/index.js
import React from 'react'
import ReactDOM from 'react-dom';
import RandomQuote from './RandomQuote';
ReactDOM.render(
  <React.StrictMode>
    <RandomQuote />
  </React.StrictMode>
  document.getElementById('root');
)// Class Component: RandomQuote
import React from "react";
class RandomQuote extends React.Component {
  constructor() {
    super();
    const quotes = [
      "May the Force be with you.",
      "There's no place like home.",
      "I'm the king of the world!",
      "My mama always said life was like a box of chocolates.",
      "I'll be back.",
    ];
    this.state = {
      quotes,
      currentQuoteIndex: this.getRandomInt(quotes.length);
    }
  }
  getRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max));
  }
  render() {
    return (
      <div>
        <h2>Random Quote</h2>
        <p>{this.state.quotes[this.state.currentQuoteIndex]}</p>
      </div>
    )
  }
}
export default RandomQuote;Updating State
We can set up event listeners in React similarly to how we did them before.
onClick is the event listener.{this.changeQuote} is the event handler method.Our Class Component File should now look like this with the new additions:
import React from "react";
class RandomQuote extends React.Component {
  constructor() {
    super();
    const quotes = [
      "May the Force be with you.",
      "There's no place like home.",
      "I'm the king of the world!",
      "My mama always said life was like a box of chocolates.",
      "I'll be back.",
    ];
    this.state = {
      quotes,
      currentQuoteIndex: this.getRandomInt(quotes.length);
    }
  }
  changeQuote = () => {
    const newIndex = this.getRandomInt(this.state.quotes.length);
    // Setting the 'new state' of currentQuoteIndex state property
    // to newly generated random index #.
    this.setState({
      currentQuoteIndex: newIndex;
    })
  }
  getRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max));
  }
  render() {
    return (
      <div>
        <h2>Random Quote</h2>
        <p>{this.state.quotes[this.state.currentQuoteIndex]}</p>
        <button type="button" onClick={this.changeQuote}>
          Change Quote
        </button>
      </div>
    )
  }
}
export default RandomQuote;Don’t modify state directly
never modify your state directly!
this.setState method to update state.Properly updating state from the previous state
In our current example, the way we have changeQuote set up leaves us with occasionally producing the same index twice in a row.
A safe method is to pass an anonymous method to this.setState (instead of an object literal)
Previous
changeQuote = () => {
    const newIndex = this.getRandomInt(this.state.quotes.length);
    this.setState({
      currentQuoteIndex: newIndex;
    })
  }Passing w/ Anon Method
changeQuote = () => {
  this.setState((state, props) => {
    const { quotes, currentQuoteIndex } = state;
    let newIndex = -1;
    do {
      newIndex = this.getRandomInt(quote.length);
    } while (newIndex === currentQuoteIndex);
    return {
      currentQuoteIndex: newIndex,
    };
  });
};Providing default values for props
props argument passed into super.// Application Entry Point
// ./src/index.js
import React from 'react'
import ReactDOM from 'react-dom';
import RandomQuote from './RandomQuote';
// Re-assign our array here and pass it in as a prop in Render.
const quotes = [
      "May the Force be with you.",
      "There's no place like home.",
      "I'm the king of the world!",
      "My mama always said life was like a box of chocolates.",
      "I'll be back.",
      "This way I can define more quotes",
    ];
ReactDOM.render(
  <React.StrictMode>
    <RandomQuote quotes={quotes}/>
  </React.StrictMode>
  document.getElementById('root');
)defaultProps!// At the bottom of RandomQuote.js...
RandomQuote.defaultProps = {
  quotes: [
    "May the Force be with you.",
    "There's no place like home.",
    "I'm the king of the world!",
    "My mama always said life was like a box of chocolates.",
    "I'll be back.",
    "This way I can define more quotes",
  ],
};import React from "react";
class AlertButton extends React.Component {
  showAlert = () => {
    window.alert("Button Clicked!");
  };
  render() {
    return (
      <button type="button" onClick={this.showAlert}>
        Submit
      </button>
    );
  }
}Preventing default behavior
HTML Elements in the browser often have a lot of default behavior.
<a> element navigates so a resource denoted by <href> property.Here is an example of where using e.preventDefault() could come in handy.
import React from "react";
class NoDefaultSubmitForm extends React.Component {
  submitForm = (e) => {
    e.preventDefault();
    window.alert("Handling form submission...");
  };
  render() {
    return (
    <form onSubmit={this.submitForm}>
      <button>Submit</button>
    </form>;
    )}
}this.submitForm method can be completed.e.preventDefault() into the actual method to get around this problem.e : Parameter that references a Synthetic Event object type.Using this in event handlers
// ./src/AlertButton.js
import React from "react";
class AlertButton extends React.Component {
  showAlert = () => {
    window.alert("Button clicked!");
    console.log(this);
  };
  render() {
    return (
      <button type="button" onClick={this.showAlert}>
        Click Me
      </button>
    );
  }
}
export default AlertButton;this we see the AlertButton object.undefined => remember that fat arrow binds to the current context!Reviewing class methods and the this keyword
class Boyfriend {
  constructor() {
    this.name = "Momato Riruru";
  }
  displayName() {
    console.log(this.name);
  }
}
const Ming = new Boyfriend();
Ming.displayName(); // => Momato Riruru
const displayAgain = Ming.displayName;
displayAgain(); // => Result in a Type Error: Cannot read property 'name' of undefined.The first time we use our displayMethod call, it is called directly on the instance of the boyfriend class, which is why Momato Riruru was printed out.
The second time it was called, the ref of the method is stored as a variable and method is called on that variable instead of the instance; resulting in a type error (it has lost it’s context)
Remember we can use the bind method to rebind context!
To continue using function declarations vs fat arrow we can assign context in a constructor within a class component.
import React from "react";
class AlertButton extends React.Component {
  constructor() {
    super();
    this.showAlert = this.showAlert.bind(this); // binding context
  }
  showAlert() {
    console.log(this);
  }
  render() {
    return (
      <button type="button" onClick={this.showAlert}>
        Submit
      </button>
    );
  }
}
export default AlertButton;Experimental Syntax : Syntax that has been proposed to add to ECMAScript but hasn’t officially been added to the language specification yet.
The SyntheticEvent object
Synthetic Event Objects: Cross Browser wrappeds around the browser’s native event.
Attributes of the Synthetic Event Object: |Attributes| |—| | boolean bubbles | |boolean cancelable| | DOMEventTarget currentTarget| |boolean defaultPrevented| |number eventPhase| |boolean isTrusted| |DOMEvent nativeEvent| |void preventDefault()| |boolean isDefaultPrevented()| |void stopPropagation()| |boolean isPropagationStopped()| |void persist()| |DOMEventTarget target| |number timeStamp| |string type |
nativeEvent : property defined in a synthetic event object that gives you access to the underlying native browser event (rarely used!)
Exercise being done in a separate file
Random Notes
onChange : detects when a value of an input element changes.
onChange to our input fields makes our component’s state update in real time during user input.preventDefault onto form submissions to deal with the default behavior of the browser refreshing the page!submittedOn: new Date(), Can be added to a form, most likely will persist into a DB.
Controlled Components
onChange event handlers on form fields to keep our component’s state as the "one source of truth"Adding an onChange event handler to every single input can massively bloat your code.
textarea is handled differently in react: it takes in a value property to handle what the inner text will be.
// ./src/ContactUs.js
import React from "react";
class ContactUs extends React.Component {
  constructor() {
    super();
    this.state = {
      name: "",
      email: "",
      phone: "",
      phoneType: "",
      comments: "",
      validationErrors: [],
    };
  }
  onChange = (e) => {
    const { name, value } = e.target;
    this.setState({ [name]: value });
  };
  // Vanilla JS Function for validating inputs
  validate(name, email) {
    const validationErrors = [];
    if (!name) {
      validationErrors.push("Please provide a Name");
    }
    if (!email) {
      validationErrors.push("Please provide an Email");
    }
    return validationErrors;
  }
  onSubmit = (e) => {
    // Prevent the default form behavior
    // so the page doesn't reload.
    e.preventDefault();
    // Retrieve the contact us information from state.
    const { name, email, phone, phoneType, comments } = this.state;
    // Get Validation Errors - proceeding destructuring values from this.state.
    const validationErrors = this.validate(name, email);
    // If we have errors...
    if (validationErrors.length > 0) {
      this.setState({ validationErrors });
    } else {
      // Proceed normally
      // Create a new object for the contact us information.
      const contactUsInformation = {
        name,
        email,
        phone,
        phoneType,
        comments,
        submittedOn: new Date(),
      };
      console.log(contactUsInformation);
      // Reset the form state.
      this.setState({
        name: "",
        email: "",
        phone: "",
        phoneType: "",
        comments: "",
        validationErrors: [],
      });
    }
  };
  render() {
    const {
      name,
      email,
      phone,
      phoneType,
      comments,
      validationErrors,
    } = this.state;
    return (
      <div>
        <h2>Contact Us</h2>
        {validationErrors.length > 0 && (
          <div>
            The following errors were found:
            <ul>
              {validationErrors.map((error) => (
                <li key={error}>{error}</li>
              ))}
            </ul>
          </div>
        )}
        <form onSubmit={this.onSubmit}>
          <div>
            <label htmlFor="name">Name:</label>
            <input
              id="name"
              name="name"
              type="text"
              onChange={this.onChange}
              value={name}
            />
          </div>
          <div>
            <label htmlFor="email">Email:</label>
            <input
              id="email"
              name="email"
              type="text"
              onChange={this.onChange}
              value={email}
            />
          </div>
          <div>
            <label htmlFor="phone">Phone:</label>
            <input
              id="phone"
              name="phone"
              type="text"
              onChange={this.onChange}
              value={phone}
            />
            <select name="phoneType" onChange={this.onChange} value={phoneType}>
              <option value="">Select a phone type...</option>
              {this.props.phoneTypes.map((phoneType) => (
                <option key={phoneType}>{phoneType}</option>
              ))}
            </select>
          </div>
          <div>
            <label htmlFor="comments">Comments:</label>
            <textarea
              id="comments"
              name="comments"
              onChange={this.onChange}
              value={comments}
            />
          </div>
          <div>
            <button>Submit</button>
          </div>
        </form>
      </div>
    );
  }
}
ContactUs.defaultProps = {
  phoneTypes: ["Home", "Work", "Mobile"],
};
export default ContactUs;validate to make our validation functions more complex.import isEmail from "validator/es/lib/isEmail";
  validate(name, email) {
    const validationErrors = [];
    if (!name) {
      validationErrors.push("Please provide a Name");
    }
    if (!email) {
      validationErrors.push("Please provide an Email");
    } else if (!isEmail(email)) {
      validationErrors.push("Please provide a valid Email");
    }
    return validationErrors;
  }Note About Client-side vs server-side validation

The lifecycle of a React component
Each Class Component has several lifecycle methods that you can add to run code at specific times.
componentDidMount : Method called after your component has been added to the component tree.componentDidUpdate : Method called after your component has been updated.componentWillUnmount : Method called just before your component is removed from the component tree.Mounting
constructor method is calledrender method is calledDOMcomponentDidMount is calledUpdating
props
render method is calledDOMcomponentDidUpdate is calledsetState is called
render method is calledDOMcomponentDidUpdate is calledUnmounting
componentDidMount will be called.Avoiding the legacy lifecycle methods
Occasionally you will encounter some deprecated lifecycle methods:
Just know they will be removed soon from React’s API, peace.
Using the class component lifecycle methods
Exercise done in sep. directory
componentDidMount lifecycle method is for fetching data from an API.