Today I Learned Notes to self about software development

    React Basics

    Basic Hello World

    // index.js
    import React from "react"
    import ReactDom from "react-dom"
    
    // Using JSX
    ReactDom.render(<h1>Hey</h1>, document.getElementById("root")
    
    ReactDom.render can only render one element. If you have sibling elements, you must wrap them in a parent element first.

    Reusable Web Components

    Sort of a fusion between Rails View partials and an Object.

    // index.js
    import React from "react"
    import ReactDOM from "react-dom"
    
    function MyApp() {
      return(
        <ul>
          <li>1</li>
          <li>2</li>
          <li>3</li>
        </ul>
      )
    }
    
    ReactDOM.render(
      <MyApp />,
      document.getElementById("root")
    )
    
    Components can also only return one element. If you have sibling elements, you must wrap them in a parent element first.

    Externalize Components

    1. Make a file with the exact name as your Component (MyApp.js for example).
    2. In the external component import react.
    3. Move your component from index.js into the file you just created.
    4. Export component (export default MyApp).
    5. Import component in index.js (import MyApp from "./MyApp"), use relative path for component, & can leave out file extension.

    Add some CSS

    You need to use className instead of class because React uses the JavaScript DOM API, which has things like

    document.getElementById("root").className
    

    So you can style things like:

    //...
    return (
      <header className="navbar">This is the header</header>
    )
    //...
    
    You can’t add className on JSX elements (the ones that look like HTML). You can’t add it to a Component

    Interpolate JS in JSX

    Just add the { }

    function App() {
      const firstName = "Jelani"
      const lastName = "Woods"
      
      return (
        <h1>Hello {firstName + " " + lastName}!</h1>
      )
    }
    

    Or in ES6

      return (
        <h1>Hello {`${firstName} ${lastName}`}!</h1>
      )
    

    Props

    Conceptually similar to attribute/value pairs of an HTML element.

    You give an <img> element a src="" to some value. That value can change and will dramatically differentiate one <img> from another.

    Relating more to components, props are arguments to a component.

    <ContactCard 
      name="Mr. Whiskerson" 
      imgUrl="http://placekitten.com/300/200" 
      phone="(212) 555-1234" 
      email="mr.whiskaz@catnap.meow"
    />
    

    Use the props in the Component.

    function ContactCard(props) {
        console.log(props)
        return (
          <div className="contact-card">
            <img src={props.imgUrl}/>
            <h3>{props.name}</h3>
            <p>Phone: {props.phone}</p>
            <p>Email: {props.email}</p>
          </div>
        )
    }
    

    State

    — is data that Component maintains

    props are immutable. Components recieve them, but they cannot change them.

    State can be changed.

    1. Component must be Class based.
    2. Add Constructor and call super()
    3. Initialize state.
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          answer: "Yes" // initial value
        }
      }
      //...
    

    Can pass state from Parent to Child Component through props.

    render() {
      return (
        <div>
          <h1>Is state important to know? {this.state.answer}</h1>
          <ChildComponent answer={this.state.answer}/>
        </div>
      )
    }
    

    Changing the state with setState() will automatically, update Child Components that received the state.

    Event handling

    Pretty much like regular JS. Camel-cased event HTML triggers, except you can write the function right there.

    function App() {
      return (
        <div>
          <img src="https://www.fillmurray.com/200/100"/>
          <br />
          <br />
          <button onClick={() => console.log("I was clicked!")}>Click me</button>
        </div>
     )
    }
    

    setState

    You can’t modify state directly and probably don’t want to.

    // BAD
    handleClick() {
      this.state.count++
    }
    

    Use setState

    You can pass setState an object that represents the new state.

    this.setState({count: 1})
    

    You can also pass a function to setState.

    this.setState(prevState => {
      // ...
    })
    

    The function must return an Object that represents the new state.

    this.setState(prevState => {
      return {
        count: prevState.count + 1
      }
    })
    

    This way you have a guarenteed way to accesss the previous state (this.state is not guaranteed to be accurate).

    TypeError: Cannot read property ‘setState’ of undefined

    Annoyingly, if you’re using a function to call setState you first need to bind this to the current class.

    constructor() {
      super()
      this.state = {
        count: 0
      }
      this.handleClick = this.handleClick.bind(this)
    }
    // ...
    // in render()
    <button onClick={this.handleClick}>Change!</button>
    

    Lifecycle Methods

    • render() - not usually labelled as such. How the Component is displayed to the world. Can be called anytime the state or props change.

    • componentDidMount() - The very first time the Component shows up, React runs this method. Will only run once. Re-renders will not trigger this. Usually used for API call.

    • componentWillReceiveProps() - DEPRECATED: Run everytime Component receives props. Also run when Parent Component sends props to Child Component.

      componentWillReceiveProps(nextProps) {
        if (nextProps.whatever !== this.props.whatever) {
          // do something important here
        }
      }
      
    • shouldComponentUpdate(nextProps, nextState) - return true/false if you want Component to re-render.

    • componentWillUnmount() - Used to teardown or clean up code before Component disappears.

    static getDerivedStateFromProps(props, state) {
      // return the new, updated state based upon the props
      // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
    }
    
    getSnapshotBeforeUpdate() {
      // create a backup of the current way things are
      // https://reactjs.org/docs/react-component.html#getsnapshotbeforeupdate
    }
    
    • componentDidUpdate() -
    componentDidUpdate() {
      const newColor = randomcolor()
      this.setState({color: newColor})
    }
    
    // Uncaught Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    

    Since the render is called anytime the state is changed and componentDidUpdate is called after render, changing the state will trigger a new render which triggers a new componentDidUpdate which updates the state which triggers the render… infinitely. Must only update state based on condition.

    componentDidUpdate(prevProps, prevState) {
        if(prevState.count !== this.state.count) {
            const newColor = randomcolor()
            this.setState({color: newColor})
        }
    }
    

    Conditional Rendering

    Ternary

    Of course, there are a LOT of different ways to do conditionals. Ternary operators, however, are pretty commonly used in React.

    expression ? when true do this : otherwise do this

    render() {
        return (
            <div>
                {this.state.isLoading ?
                <h1>Loading...</h1> :
                <Conditional />}
            </div>
        )
    }
    

    Logical AND Operator

    Usually, we think of && when writing some conditional and you only want the condition to be true if both expressions are true.

    How JavaScript works under the hood in regards to &&:

    • the first expression on the left will be evaluated
    • if the first expression is not true the whole condition is false
    • if the first expression is true, the second expression is immediately returned

    This allows us to do some cleverness with conditional Component rendering.

    Say we have a list of unread messages in state and we only want to display them if the list has messages.

    Using a Ternary we could have something like

    {
        this.state.unreadMessages.length > 0 ? 
        <h2>You have {this.state.unreadMessages.length} unread messages!</h2> :
        null
    }
    

    But using the logical AND, we can do

    render() {
        return (
            <div>
                {
                    this.state.unreadMessages.length > 0 && 
                    <h2>You have {this.state.unreadMessages.length} unread messages!</h2>
                }
            </div>
        )
    }
    

    Since if this.state.unreadMessages.length > 0 is true, then <h2>You have {this.state.unreadMessages.length} unread messages!</h2> will immediately be returned.

    Can also do conditional styling

    function TodoItem(props) {
        const completedStyle = {
            fontStyle: "italic",
            color: "#cdcdcd",
            textDecoration: "line-through"
        }
        
        return (
            <div className="todo-item">
                <input 
                    type="checkbox" 
                    checked={props.item.completed} 
                    onChange={() => props.handleChange(props.item.id)}
                />
                <p style={props.item.completed ? completedStyle: null}>{props.item.text}</p>
            </div>
        )
    }
    

    fetch

    A Global vanilla JS function that makes HTTP calls. Docs

    Promises

    Article

    Reading from APIs

    Often done in componentDidMount(),

    componentDidMount() {
        fetch("https://swapi.co/api/people/1")
            .then(response => response.json())
            .then(data => console.log(data))
    }
    

    and of course we store the data in state somewhere.

    Usually, it’s good practice to display some loading indicater if you’re reading from an API

    componentDidMount() {
        this.setState({loading: true})
        fetch("https://swapi.co/api/people/1")
            .then(response => response.json())
            .then(data => {
                this.setState({
                    loading: false,
                    character: data
                })
            })
    }
    

    Forms

    Docs

    Unlike other DOM elements in React, forms naturally keep some internal state.

    Usually we have a JavaScript function that handles the submission of the form and has access to the data that the user entered into the form.

    Instead of waiting for the User to submit the form before we validate and check the inputs, These Controlled Components will actually update the state after every keystroke.

    • Make statethe single source of truth.
    import React, {Component} from "react"
    
    class App extends Component {
        constructor() {
            super()
            this.state = {
                firstName: "",
                lastName: ""
            }
            this.handleChange = this.handleChange.bind(this)
        }
        
        handleChange(event) {
            const {name, value} = event.target
            this.setState({
                [name]: value
            })
        }
        
        render() {
            return (
                <form>
                    <input 
                        type="text" 
                        value={this.state.firstName} 
                        name="firstName" 
                        placeholder="First Name" 
                        onChange={this.handleChange} 
                    />
                    <br />
                    <input 
                        type="text" 
                        value={this.state.lastName} 
                        name="lastName" 
                        placeholder="Last Name" 
                        onChange={this.handleChange} 
                    />
                    <h1>{this.state.firstName} {this.state.lastName}</h1>
                </form>
            )
        }
    }
    
    export default App
    
    

    Different types of inputs

    textarea

    In React a <textarea /> is more similar to an <input>, it’s a self-closing tag. We can use the value property

    checkbox

    handleChange(event) {
        const {name, value, type, checked} = event.target
        type === "checkbox" ? this.setState({ [name]: checked }) : this.setState({ [name]: value })
    }
    
    <label>
        <input 
            type="checkbox" 
            name="isFriendly"
            checked={this.state.isFriendly}
            onChange={this.handleChange}
        /> Is friendly?
    </label>
    

    radio

    <label>
        <input 
            type="radio" 
            name="gender"
            value="female"
            checked={this.state.gender === "female"}
            onChange={this.handleChange}
        /> Female
    </label>
    

    select

    <label>Favorite Color:</label>
    <select 
        value={this.state.favColor}
        onChange={this.handleChange}
        name="favColor"
    >
        <option value="blue">Blue</option>
        <option value="green">Green</option>
        <option value="red">Red</option>
        <option value="orange">Orange</option>
        <option value="yellow">Yellow</option>
    </select>
    

    We can write a single handleChange for every type of input in our forms.

    Container and Presentation Components

    Nicer separation of concerns!

    Container Component

    Job is to maintain state, methods that update state. Delegate presentation logic to other component.

    Presentation Component

    Job is to recieve props and displaying things correctly

    article

    Example,

    Container Component delegates the state data to Presentation Component

    // Container
    render() {
        <FormComponent
            handleChange={this.handleChange}
            data={this.state}
        />
    }
    
    // Presentation
    
    <input 
      name="firstName" 
      value={props.data.firstName}
      //...
    

    Or with Object spread

    render() {
        <FormComponent
            handleChange={this.handleChange}
            {...this.state}
        />
    }
    

    Using Arrow Functions

    Using regular function syntax, we need to bind this.

    class App extends Component {
        constructor() {
            super()
            // ...
            this.handleChange = this.handleChange.bind(this)
        }
        
        handleChange(event) {
            this.setState({
              // ..
            })
        }
    

    But if we use the Arrow Function syntax, this is taken from the surroundings, which in this context means we don’t need to bind it.

    class App extends Component {
        constructor() {
            // ..
        }
        
        handleChange = (event) => {
            const { name, value } = event.target
            this.setState({
              // ..
            })
        }
    

    Other modern/advanced React features/topics to learn:

    • Official React Context API - https://reactjs.org/docs/context.html
    • Error Boundaries - https://reactjs.org/docs/error-boundaries.html
    • render props - https://reactjs.org/docs/render-props.html
    • Higher Order Components - https://reactjs.org/docs/higher-order-components.html
    • React Router - https://reacttraining.com/react-router/core/guides/philosophy
    • React Hooks - https://reactjs.org/docs/hooks-intro.html
    • React lazy, memo, and Suspense - https://reactjs.org/blog/2018/10/23/react-v-16-6.html

    Hooks

    Hooks are a way to “hook into” state and lifecycle methods of Components without using classes.

    = Allows us to only use functional Components

    • Improve readability and organization of components

    useState

    import React, { useState } from "react"
    
    function App() {
        const value = useState()
        console.log(value)
        
        return (
            <div>
                <h1>Is state important to know? Yes</h1>
            </div>
        )
    }
    

    useState() returns [ null, f()]

    useState() returns [true, f()]

    The argument to useState is the initial value of state

    Why does it return an Array?

    We’re expected to do Array destructuring.

    Object Destructuring
    const person = {
      name: "Joe",
      age: 42
    }
    
    const { name, age } = person
    
    Array Destructuring
    const [ value ] = [ "No", f()]
    value // => "No"
    

    You can name value, whatever you want! (a difference between object destructuring)

    Note: When using useState we no longer need to initialize state as a full object. It can be a primitive value.

    Changing state

    The f()returned from useState is the function that update the state you just set. Usually the name of this function is set + “name of whatever state was set.

    const [count, setCount] = useState(0)
    ...
    // bad
    <button onClick={() => setCount(prevCount => prevCount + 1)}>Change!</button>
    ...
    // better
    function increment() {
      setCount(prevCount => prevCount + 1)
    }
    ...
    <button onClick={increment}>Increment</button>
    

    If your state is going to be a big Hash or Array, you need to provide the entire object when updating state. Hooks don’t do the nice thing that setState did, of just updating the part of state that changed. You can use object spread and it’s not bad.

    You can also just make multiple states— wait what does that EVEN mean?

    const [count, setCount] = useState(0)
    const [answer, setAnswer] = useState("Yes")
    

    use Effect

    Hook into a component’s lifecycle method.

    Replacement for

    • componentDidMount
    • componentDidUpdate
    • componentWillUnmount

    Hook that let’s produce side effects of our component.

    Anything that reaches outside the Component to do something:

    • Network request
    • DOM manipulation
    • Even listeners or timeouts and intervals

    useEffect(callback, Array) - typically update state in callback function. The values in the Array are the state values that useEffect will “watch” and trigger the callback to run. If Array is empty, useEffect acts like componentDidMount and only runs once.

    #react #js