Skip to content
herougo edited this page Apr 29, 2025 · 33 revisions

YouTube Playlist by codevolution

Source

Prerequisites

  • HTML, CSS, and JavaScript fundamentals
  • ES6 - let and const, arrow functions, template literals, default parameters, object literals, rest and spread operators, and destructuring assignment
  • JavaScript - 'this' keyword, filter, map, and reduce
let myFunction = (a, b) => a * b;                  // arrow function
let text = `Welcome ${firstName}, ${lastName}!`;   // template literals (use back-ticks)
const candidate = {                                // object literal
  title: "Mr.",
  firstname: "Tanka"
};
const candidate2 = {...candidate, firstname: "Guy"}// spread operator
function sum(...theArgs) {                         // rest operator
  let total = 0;
  for (const arg of theArgs) {
    total += arg;
  }
  return total;
}
var x, y;
[x, y] = [10, 20];                                 // destructuring assignment

Hello World (Deprecated as of February 2025)

npx create-react-app <project_name>
cd <project_name>
npm start

Creates a project with the following (and more):

  • public/index.html
    • shouldn't need to modify this file
  • public/manifest.json
  • package.json
    • contains dependencies
  • src

Alternate Hello World

Vite is a frontend build tool and development server designed for speed and simplicity in modern web development.

npm create vite@latest my-react-ts-app -- --template react-ts
npm install
npm run dev

Explanations of Boilerplate Files

  • vite-env.d.ts: helps TypeScript recognize the custom environment variables prefixed with VITE_ that you define in your .env file
  • tsconfig.node.json: used by vite itself
  • tsconfig.app.json: used by your app in the src folder

Updated

Components

Keyboard Shortcuts

  • install the "JS JSX Snippets Extension" in VS Code
  1. rfc + tab -> functional component
  2. rcc + tab -> class component

Class Components

Use

<Welcome name="Bruce">
  <div>child</div>
</Welcome>

Definition

note the following

  • this code should set the greeting from "Welcome" to "Hello" when a button is clicked
  • access of props.name
  • state of greeting (constructor and this.state.greeting)
  • never modify this.state outside the constructor (use this.setState instead)
  • button onClick methods
    • avoid approach 3 because of performance issues
    • react docs recommend approach 4 or 5
import React, { Component } from 'react';

class Welcome extends Component {
    constructor() {
        super()
        this.state = {
            greeting: 'Welcome'
        }
        this.clickHandler2 = this.clickHandler2.bind(this)
    }

    clickHandler1() {
        const callback = () {
            console.log(this.state.greeting);
        }
        this.setState({
            greeting: 'Hello'
        }, callback)
    }

    clickHandler2() {
        this.setState({
            greeting: 'Hello'
        })
    }

    clickHandler3 = () => {
        this.setState({
            greeting: 'Hello'
        })
    }

    render() {
        return (
            <div>
                <h1>{this.state.greeting} {this.props.name}</h1>
                <button onClick={() => this.clickHandler1()}>Say Hello Instead 1</button>    {/* works */}
                <button onClick={this.clickHandler1}>Say Hello Instead 2</button>            {/* doesn't work */}
                <button onClick={this.clickHandler1.bind(this)}>Say Hello Instead 3</button> {/* works */}
                <button onClick={this.clickHandler2.bind(this)}>Say Hello Instead 4</button> {/* works */}
                <button onClick={this.clickHandler3}>Say Hello Instead 5</button>            {/* works */}
                {this.props.children}
            </div>
        );
    }
}

export default Welcome; // import using 'import Welcome from "Welcome"'

Functional Components

Use

<Hello name="Bruce">
  <div>child</div>
</Hello>

Definition

import React from 'react';

const Hello = (props) => {  // can omit props on this line if not needed
    // alternatively use below
    // const {name} = props;
    return (
        <div>
            <h1>Hello {props.name}</h1>
            {props.children}
        </div>
    );
}

export default Hello;

Props

  • (See above)
  • props are immutable in a component

State

When the state depends on the previous state and you want to call setState, pass a function to setState. This is because otherwise, React might group multiple calls together and do them all in one go.

    ...
    increment() {
        const countIncrementer = (prevState, props) => ({
            count: prevState.count + 1
        })
        this.setState(countIncrementer);
    }
    ...

Methods as Props

Parent

<ChildComponent greetHandler={this.greetParent} />

Child

...
<button onCLick={props.greetHandler}>Greet Parent</button>
...

List Rendering and Keys

  • key is needed when rendering lists due to how React checks to see what needs to be re-rendered
const personList = persons.map(person => <Person key={person.id} person={person} />)
return <div>{personList}</div>

When acceptable to use index as key?

  1. Items do not have a unique ID
  2. List is static (won't change)
  3. List will never be reordered or filtered

CSS

myStyle.css

.primary {
    color: orange
}

appStyles.module.css

.error {
    color: red
}

Stylesheet.js

...
import './myStyle.css'  # children components also see this (can lead to conflicts)
import styles from './appStyles.module.css'

const heading = {
    fontSize: '72px',
    color: 'blue'
}

function StyleSheet() {
    return <div>
        <h1 className='primary'>Stylesheets</h1>
        <h1 style={heading}>Inline</h1>
        <h1 className='styles.error'>Modules</h1>
    </div>
}

...

Form Handling

Can use textarea, select

...
    handleUsernameChange = event => {
        this.setState({
            username: event.target.value
        })
    }
...

Component Lifecycle Methods

  • Mounting: when an instance of a component is being created and inserted into the DOM
    • constructor, static getDerivedStateFromProps, render, and componentDidMount
  • Updating: When a component is being re-rendered as a result of changes to either its props or state
    • static getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, and componentDidUpdate
  • Unmounting: When a component is being removed from the DOM
    • componentWillUnmount
  • Error Handling: When there is an error during rendering in a lifecycle method, or in the constructor of any child component
    • static getDerivedStateFromError, and componentDidCatch

Mounting

Method Use Do Don't
constructor - initialize state
- bind event handlers
- cause side effects (e.g. HTTP requests)
static getDerivedStateFromProps when state depends on changes in props over time - set state - cause side effects
render only required method - read props and state and return JSX - change state, make Ajax calls, or interact with the DOM
componentDidMount invoked after a component and all of its children components have been rendered to the DOM - cause side effects

Order of Methods

  1. Parent Constructor
  2. Parent getDerivedStateFromProps
  3. Parent render
  4. Child Constructor
  5. Child getDerivedStateFromProps
  6. Child render
  7. Child componentDidMount
  8. Parent componentDidMount

Updating

Method Use Do Don't
static getDerivedStateFromProps called every time a component is re-rendered - set state - cause side effects
shouldComponentUpdate self-explanatory - performance optimization - cause side effects
render (see previous table) (see previous table) (see previous table)
getSnapshotBeforeUpdate called right before changes from the virtual DOM are reflected in the DOM - capture some info from the DOM
componentDidUpdate (prevProps, prevState, snapshot) called after the render finished cause side effects
  • static getDerivedStateFromProps is rarely used in the update lifecycle

Error Handling

Method Use Do Don't
static getDerivedStateFromError(error) ?
componentDidCatch(error, info) ?

Unmounting

Method Use Do Don't
componentWillUnmount invoked immediately before component is destroyed cancel network requests, remove event handlers call setState

Fragments

  • when you want to have a component with multiple elements, but don't what to wrap them in a div
    ...
    return (
        <React.Fragment> {items.map(item => ...)} </React.Fragment>
    )

Equivalent syntax for convenience

    ...
    return (
        <> {items.map(item => ...)} </>
    )

Pure Components

import React, { PureComponent } from 'react'

class PureComp extends PureComponent {
  ...
}

export default PureComp

Regular component

  • does not implement the shouldComponentUpdate method (always return true by default)

Pure Component

  • implements shouldComponentUpdate with a shallow props/state comparison

Tip

  • use regular components most of the time unless there is a performance hit

Memo

  • equivalent of Pure Component for functional components
import React from 'react'

function MemoComp({name}) {
  ...
}

export default React.memo(MemoComp)

Ref

  • allows you to interact with the DOM
import React, { Component } from 'react';

class RefsDemo extends Component {
    constructor() {
        super()
        this.inputRef = React.createRef()
    }

    componentDidMount() {
        this.inputRef.current.focus()
    }

    clickHandler = () => {
        alert(this.inputRef.current.value)
    }

    render() {
        return (
            <div>
                <input type="text" ref={this.inputRef} />
                <button onClick={this.clickHandler}>Click</button>
            </div>
        );
    }
}

export default RefsDemo;

Note

  • can attach ref to a component and use that ref to call component methods (e.g. this.compRef.focusMe())
  • refs can't be attached to functional components

Ref Forwarding

  • way to pass Refs to functional components
  • rarely used
const FRInput = React.forwardRef((props, ref) => {
  return (
    <div>
      <input type="text" ref={ref} />
    </div>
  )
})

Portals

  • Normally, components are rendered under the "root" node defined in the HTML
  • Portals allow you to render components in nodes outside this "root" node
class App extends Component {
  render() {
    return (
      <div className="App">
        <PortalDemo />
      </div>
    )
  }
}
import ReactDOM from 'react-dom'

function PortalDemo() {
  return ReactDOM.createPortal(
    <h1>Portals demo</h1>,
    document.getElementById('portal-root')
  )
}

Error Boundary

  • Error Boundary: class component that implements one or both of the following lifecycle methods.
    • static getDerivedStateFromError(error): used to render a fallback UI after an error is thrown
    • componentDidCatch(error, info): used for logging error information
  ...
    this.state = {
      hasError: false
    }
  }

  static getDerivedStateFromError(error) {
    return {
      hasError: true
    }
  }

  render() {
    if (this.state.hasError) {
      return <h1> Something went wrong</h1>
    }
    return this.props.children
  }
<ErrorBoundary>
  ...
</ErrorBoundary>

Higher-Order Component

  • a pattern where a function takes a component as an argument and returns a new component
  • can be used to add new props to the input component
  • used for code reuse
const NewComponent = higherOrderComponent(originalComponent)
// withCounter.js
import React from 'react'

const withCounter = WrappedComponent => {
  class WithCounter extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
        count: 0
      }
    }
    incrementCount = () => {
      this.setState(prevState => {
        return { count: prevState.count + 1 }
      }
    }
    render() {
      return (
        <WrappedComponent
          count={this.state.count}
          incrementCount={this.incrementCount}
          { ... this.props} /*                 Please remember this!! */
        />
      )
    }
  }
  return WithCounter
}
export default withCounter

// ClickCounter.js
import React, { Component } from 'react'
import withCounter from 'withCounter'

class ClickCounter extends Component {
  render() {
    const { count, incrementCount } = this.props
    return (
      <button onClick={incrementCount}>Clicked {count} times</button>
    )
  }
}

export default withCounter(ClickCounter)

// App.js
...
  <ClickCounter />
...

Render Props

  • render prop: refers to a technique for sharing code between React components using a prop whose value is a function
// App.js
...
    <Counter
        render={(count, incrementCount) => (
            ClickCounterTwo count={count} incrementCount={incrementCount} />
        )}
    />
...

// Counter.js
import React from 'react'

class Counter extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
    }
    incrementCount = () => {
        this.setState(prevState => {
            return { count: prevState.count + 1 }
        }
    }
    render() {
        return (
        <div>
            {this.props.render(this.state.count, this.incrementCount)}
        </div>
        )
    }
}

export default Counter

Context

  • Context: provides a way to pass data through the component tree without having to pass props down manually at every level
// userContext.js

import React from 'react'

const UserContext = React.createContext() // can use React.createContext('default value')
const UserProvider = UserContext.Provider
const UserConsumer = UserContext.Consumer

export default { UserProvider, UserConsumer }

// App.js
...
    <UserProvider value="Vishwas">
      <AncestorComponent />
    </UserProvider>
...

// DescendentComponent.js

    render() {
      <UserConsumer>  /* pass in function as the child */
        {username => {
          return <div> {username}</div>
        }}
      </UserConsumer>
    }
...

ContextType ??????? (Omitted)

axios GET Request

import axios from 'axios'

axios.get('...')
  .then(response => {
    console.log(response)
  })
  .catch(error => {
    console.log(error)
  })

Clone this wiki locally