Study Guide 2023+

react

Warning: These notes are partial, ongoing, incomplete, and may contain typos/inaccuracies. (They are kept factually accurate, time permitting.)

They are being united from many disparate notes created in the past and the layout/organization will gradually improve with time!

Please view them on a computer as they are not optimized for mobile (although you can still view them on Mobile along with the Flashcards at your own risk)!

Topics and code examples are lazy-loaded and may require two-clicks from the TOC to correctly calculate the updated x,y coordinates (after rendering). Thanks!

React: Lifecycle

Can be loosely divided into three primary stages:

Re-rendering included as needed based on the render operation called. (Consult the detailed Component API reference: https://reactjs.org/docs/react-component.html.) A more specific breakdown:

Mounting – key methods:

  1. constructor()
  2. render()
  3. componentDidMount()

Updating – key methods, rerendering:

  1. shouldComponentUpdate()
  2. render()
  3. componentDidUpdate()
  4. setState()
  5. forceUpdate()

Unmounting – key methods:

  1. componentWillUnmount()

Code samples:

  1. https://github.com/Thoughtscript/x_team_wp_react/tree/master/xteamClient
  2. https://gitlab.com/Thoughtscript/budget_helper/-/tree/main/client

React: Virtual and Shadow DOM

The Shadow DOM is now part of JavaScript's Web APIs:

A Virtual DOM is sometimes used in parallel with or on top of the common Shadow DOM:

React used to support a React-specific Virtual DOM as an additional React feauture:

Vue and Angular support using the Shadow DOM:

  1. https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM
  2. https://en.wikipedia.org/wiki/Virtual_DOM
  3. https://vuejs.org/guide/extras/rendering-mechanism
  4. https://legacy.reactjs.org/docs/faq-internals.html
  5. https://www.geeksforgeeks.org/reactjs-virtual-dom/
  6. https://dev.to/zokizuan/angular-adventure-deep-dive-into-angulars-view-encapsulation-ml
  7. https://www.npmjs.com/package/vue-shadow-dom
  8. https://vuejs.org/guide/extras/web-components

React: State, Props

  1. Props - immutable, settings, configuration, initialized values, etc. that are passed one-way (unidirectionally) from a wrapping parent element into a child element.
  2. State - the mutable, local Component state.

Props

Consider the following example. A component CustomFooter has several child Components namely CustomLink.

Values are supplied for url and label which are passed from CustomFooter into each CustomLink as Props.

// CustomFooter

import React from 'react'
import CustomLink from '../CustomLink'
import './CustomFooter.css'

export default () =>
    <footer>
        <ul>
            <li><CustomLink url={'https://www.linkedin.com/in/adamintaegerard/'} label={'LinkedIn'}/></li>
            <li><CustomLink url={'https://thoughtscript.io/landing.html'} label={'Thoughtscript.io'}/></li>
        </ul>
    </footer>

CustomLink then accesses the supplied Props through destructuring.

It then uses the supplied values for url and label to populate the href and text values for the rendered Anchor tag:

// CustomLink

import React from 'react'

export default ({url, label}) => <a href={url} rel="nofollow noopener noreferrer" target="_blank">{label}</a>

State

Consider the following example:

  1. AccountSummaries initializes its state Object within the constructor call.
  2. A token is made via makeToken() to authenticate against the endpoint BUDGET_HELPER_API_URL.
  3. An async HTTP GET Request is made against the endpoint BUDGET_HELPER_API_URL.
  4. The Response Object is parsed and checked for a valid Status Code.
  5. The resultant data is then set into State, updating the Component state Object.
  6. Setting or modifying State typically involves the Component re-rendering.
  7. Values in State are destructured const { accounts, authorized } = this.state and made available during Render.
import React from 'react'
import './AccountSummaries.css'
import { asyncGet } from '../../../Helpers/Xhr/Get'
import { BUDGET_HELPER_API_URL } from '../../../Constants'
import { makeToken, parseJson } from '../../../Helpers/Generic'

export class AccountSummaries extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            accounts: [],
            authorized: true,
            ...this.props
        }
    }

    componentDidMount() {
        try {           
            asyncGet(`${BUDGET_HELPER_API_URL}accounts?auth=${makeToken()}`).then(accounts => {
                if (JSON.parse(accounts).status === 200) {
                        this.setState({
                            accounts: parseJson(accounts),
                            authorized: true
                        })
                    })
                } else {
                    this.setState({
                        authorized: false
                    })
                }
            })
        } catch (ex) {
            console.log(ex)
        }
    }

    render() {
        const { accounts, authorized } = this.state
    }
}
  1. https://web.archive.org/web/20230128043222/https://x-team.com/blog/react-reactor-passwordless-spring/

Code samples:

  1. https://github.com/Thoughtscript/react_2021/tree/master/reactAppSrc/Components/Presentation
  2. https://github.com/Thoughtscript/x_team_wp_react/tree/master/xteamClient
  3. https://gitlab.com/Thoughtscript/budget_helper/-/tree/main/client

React: Hooks

Comparisons

  1. State :
    • Holds state in a single Component.
    • Doesn't persist data between Component Mounts.
    • Persists data between Rerenders, Renders.
    • Mutable.
    • Can be used as Hook but can be used in fully-qualified Class syntax.
  2. Reducer:
    • Can be reused across Components.
    • Doesn't persist data between Component Mounts.
    • Persists data between Rerenders, Renders.
    • Mutable.
    • Must be a Hook.
  3. Context:
    • Simplifies Prop-Drilling and allows top-level state to be shared with deeply nested child Components.
    • Persists and shares data between Rerenders, Renders.
    • Mutable.
    • Must be a Hook.
  4. Web Storage API (localStorage, sessionStorage) or Client-Side Database (lowdb, IndexedDB API):
    • Persists data between Rerenders, Renders, and Component Mounts / Component Unmounting.
    • Can be subscribed to using the Hook useSyncExternalStore.

State

One can now set State within a formerly Stateless Functional Component.

import React, { useState } from 'react'

export function App(props) {
  const [stateVar, setStateVar] = useState(0)

   return (
       <div>
            <button onClick={() => {
              setStateVar(stateVar + 1)
              console.log(stateVar); //0 1 2 3 ...

            }}><code>stateVar</code> Counter</button>
        </div>
  );
}

Note that stateVar must be initializated in useState.

Side Effects

Side Effects are run after a Components renders (hence, side-effect).

import React, { useEffect, useRef } from 'react'

export function App(props) {
    const myRef = useRef(null)

    useEffect(() => {
        myRef.current.style.color = "Red"
        myRef.current.className = "Example"
        console.log("side-effect") // side-effect
        console.log(myRef.current.className) // Example
    })

    return (
        <div>
            <h1 ref={myRef}>My Header</h1>
        </div>
    )
}

Useful to make succinct changes within Stateless Functional Components ("dummy components") post-render using terse/less verbose syntax (no Class constructor initialization, componentDidMount, etc.).

Reducers

Reducers are now supported out of the box and don't have to be associated with an underlying State Provider.

React continue to use this nomenclature to draw comparisons to the prior Map-Reduce naming convention (e.g. - converging sources to a single Reducer).

export const exampleStateInitialization = {
  id: 0,
  text: 'Hi!'
}

export function exampleReducer(state, action) {
  switch (action.type) {
    case 'action_type_a': {
      return {
        ...state,
        id: action.id,
        text: action.text,
      };
    }
    case 'action_type_b': {
      return {
        ...state,
        text: action.text,
      };
    }
    case 'action_type_b': {
      return {
        ...state,
        text: 'LOREM_IPSUM',
      };
    }
    default: {
      throw Error('Unknown action type: ' + action.type);
    }
  }
}
import React, { useReducer } from 'react'
import { exampleStateInitialization, exampleReducer } from './exampleReducer'

export function App(props) {
  // Define a Dispatcher function name to call the Reducer with a specific Event.
  const [exampleReducerState, exampleReducerDispatch] = useReducer(exampleReducer, exampleStateInitialization)
  return (
    <div>
      <h2 onClick = {(e) => {
        exampleReducerDispatch({
          id: 1,
          type: "action_type_b",
          text: "event_text"
        })
     }}>My Button Header</h2>
    </div>
  )
}

Context

import { createContext } from 'react'

// Create and export
export const ExampleContext = createContext("Me, Myself, and I")
import React, { createContext } from 'react'
import { ExampleComponent } from './ExampleComponent'
import { ExampleContext } from './ExampleContext'

// Import and wrap elments
export App(props) =>
  <div>
    <ExampleContext.Provider value={ "You and only you" }>
      <ExampleComponent />
    </ExampleContext.Provider>
  </div>
import React, { useContext } from 'react'
import { ExampleContext } from './ExampleContext'

export function ExampleComponent(props) {
  // Import and use the context  without passing specific Props everywhere and in-between!
  const exampleContextVal = useContext(ExampleContext)

  return (
    <div>
      <h1>{ exampleContextVal }</h1>
    </div>
  )
}

Both updating a React context value and making a React context updateable (from within a sub-Component) involves associating State with the Provider value:

export function App(props) {
  const [example, setExample] = useState("Me, Myself, and I")

  return (
    // Associate the state field here
    <ExampleContext.Provider value={example}>

      <Button onClick={() => { setExample("You and only you") }}>
        I'm Button Text
      </Button>

    </ExampleContext.Provider>
  )
}
  1. https://reactjs.org/docs/hooks-intro.html
  2. https://reactjs.org/docs/hooks-effect.html
  3. https://react.dev/learn/extracting-state-logic-into-a-reducer
  4. https://react.dev/learn/passing-data-deeply-with-context
  5. https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

Code samples:

  1. https://github.com/Thoughtscript/react_2021
  2. https://github.com/Thoughtscript/mearn_2024
  3. https://gitlab.com/Thoughtscript/region_risk_helper

React: Refs

There are two primary ways to create refs (a reference to a DOM Element that's accessible within a React Component):

  1. React.createRef() - primarily used when initializing refs within a React.Component Class constructor:

    import React from 'react'
    
    export default class App extends React.Component {
       constructor(props) {
          super(props);
          this.header = React.createRef();
       }
    
       render() {  
          console.log(this.header) 
          // current: <h1>My Header</h1>
    
          return (
             <div className="wrapper">
                <h1 ref={this.header} onClick={() => {
                   this.header.current.className = 'active'
                   this.header.current.innerText = "My New Header"
                   this.header.current.style.color = "Red"
    
                   console.log(this.header) 
                   // current: <h1 class="active" style="color: red;">My New Header</h1>
                }}>My Header</h1>
             </div>
          )
       }
    }
    
  2. const myRef = useRef(null); - useful to both access a DOM Element that's accessible within a React Component with an initialized value while keeping the value around.

    Use with Stateless Functional Components ("dummy components").

    import React, {useRef} from 'react';
    
    export default () => {
       const myRef = useRef(null)
    
       console.log(myRef)
       // current: <h1>My Header</h1>
    
       return (
          <div>
             <h1 ref={myRef}  onClick = {(e) => {
                myRef.current.innerHTML = "My New Header"
                myRef.current.style.color = "Red"
                console.log(myRef)
                // current: <h1 style="color: red;">My New Header</h1>
    
             }}>My Header</h1>
          </div>
       )
    }
    

Current

  1. Most refs will expose their attributes after the Component is rendered (e.g. - in a Side Effect or onClick).
  2. Use current to access the current Element State.
  1. https://reactjs.org/docs/react-api.html
  2. https://reactjs.org/docs/hooks-reference.html#useref

Code samples:

  1. https://gitlab.com/Thoughtscript/card_lookup_helper/-/blob/main/client/reactAppSrc/Components/Navigation/Menu/index.jsx

React: Parcel

Build-tool and configuration notes.

npm run build-parcel-prod
npm run build-parcel
cd dist
npx serve

One gotcha: parcel-bundler/parcel#7636. If you add to package.json:

  "engines": {
    "node": "=16.17.0"
  }

You'll get: @parcel/packager-js: External modules are not supported when building for browser. Remove the engines field.

By default, NPX and parcel will serve from: http://localhost:1234/

React: Redux

Overview

Redux provides asynchronous, multi-component, horizontal, State Management for complex single page React apps.

  1. Actions - defines the available operations on state storage.
  2. Container Component - binds Actions to Props. Call the bound Actions within the component to modify the Reducer state.
  3. Reducer - state storage.

Example

Actions:

'use strict'

/**
 *  Default actions for interacting with various Redux stores.
 *
 *  @Author - Adam InTae Gerard - https://www.linkedin.com/in/adamintaegerard/
 */

export const UNSAFE_SAVE = 'UNSAFE_SAVE'
export const REMOVE = 'REMOVE'
export const GET = 'GET'
export const CLEAR = 'CLEAR'
export const SAFE_SAVE = 'SAFE_SAVE'

//Use for public info containing no sensitive information
export const unsafeSave = v => {
  return {type: UNSAFE_SAVE, v}
}

//Use for secure or private info
export const safeSave = v => {
  return {type: SAFE_SAVE, v}
}

export const remove = v => {
  return {type: REMOVE, v}
}

export const get = v => {
  return {type: GET, v}
}

export const clear = v => {
  return {type: CLEAR, v}
}

Stateful Container Component:

'use strict'

/**
 *  Page Container.
 *
 *  @Author - Adam InTae Gerard - https://www.linkedin.com/in/adamintaegerard/
 */

import { connect } from 'react-redux'
import { Page } from './Page'
import { clear, get, remove, safeSave } from '../../../Redux/Shared/DefaultActions'

const mapStateToProps = state => {
  return {
    ...state
  }
}, mapDispatchToProps = dispatch => {
  return {
    save: (key, s) => {
      dispatch(safeSave({data: s, index: key}))
    },
    remove: key => {
      dispatch(remove({index: key}))
    },
    clear: () => {
      dispatch(clear())
    },
    get: key => {
      dispatch(get({index: key}))
    }
  }
}

export const PageContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(Page)

Reducer:

'use strict'

/**
 *  Encapsulated state storage Reducer.
 *
 *  @Author - Adam InTae Gerard - https://www.linkedin.com/in/adamintaegerard/
 */

import { CLEAR, GET, REMOVE, SAFE_SAVE } from '../Shared/DefaultActions'

let encapsulatedStateObj = {}

const set = (index, data) => {
  encapsulatedStateObj[index] = data
  return Object.assign({}, encapsulatedStateObj)
}, remove = index => {
  delete encapsulatedStateObj[index]
  return Object.assign({}, encapsulatedStateObj)
}, clear = () => {
  for (let i = 0; i < Object.getKeys(encapsulatedStateObj).length; i++) {
    remove(Object.getKeys(encapsulatedStateObj)[i])
  }
  return Object.assign({}, encapsulatedStateObj)
}

/**
 * Default supplied reducer.
 *
 * Caches into state.
 *
 * Partition by key in state.
 *
 * @param state
 * @param action
 * @returns {*}
 * @constructor
 */

export const SafeStorage = (state = encapsulatedStateObj, action) => {
  const type = action['type']
  switch (type) {
    case SAFE_SAVE:
      return set(action['v']['index'], action['v']['data'])
    case REMOVE:
      return remove(action['v']['index'])
    case GET:
      return Object.assign({}, encapsulatedStateObj[action['v']['index']])
    case CLEAR:
      return clear()
    default:
      return state
  }
}

Code samples:

  1. https://github.com/Thoughtscript/x_team_wp_react/tree/master/xteamClient/reactAppSrc