React 18 Cheatsheet - adonisv79/bytecommander.com GitHub Wiki

React JS 18+ Cheatsheet

Basic Functional Component

import { useState, useEffect, useMemo } from 'react'

export interface MyComponentProps {
  prop1: string
  prop2: number
  children: any
}

const MyComponent: FC<MyComponentProps > = ({ prop1, prop2, children}) => {
  const [ stateVar, setStateVar ] = useState(0)
  // use useMemo to cache previous results and update only when certain dependencies change
  let cachedResult = useMemo(() => {
    return numberToString(prop2)
  }
  , [prop2])
  
  useEffect(() => {
    // do something when prop1 changes
    return () => {
      // do something when the component unmounts
    }
  }, [prop1])

  function handleClick() {
    alert(prop1)
    setStateVar(stateVar+1)
  }

  return <>
    <div>Hello World</div>
    <div>{children}</div>
    <div>The number is {prop2} and the string is <button onclick={handleClick}>Clicked {stateVar} times</button>
    </div>
  </>
}

Rules:

  • Functional components should remain pure. it must not modify or control anything outside of it. To do that use effects (look for "side effects")
  • useState hooks returns an array with 2 items.
    • state Object/Variable - treated as mutable (do not modify directly). This is the storage of the value which in this case is the number 0
    • state setter - this is the funcion to call to set the state Object/Variable. using it forces a re-render of the associated component.
  • the values of useState should be named sensibly with the purpose or content of the state variable while the setter uses the same name preceded by 'set'
  • state can be accessed anywhere but should not be "set" inside of side effects unless handled properly.
  • useMemo is for further performance optimization. do not use unless needed and has actual impact. If the value often should not change on each render or the computation is really slow, then consider using it.

useState

const [ myState, setMyState] = useState({ x: 20, y: 10})

handleChangeX(newX) {
  // this function version of "state setter" is used when potential state modifications can be called in sequence.
  // setState(newX) will not apply if steState(newY) is called as myState at the time each setter is called is the same.
  setState((currentValue) => { return { { x: newX } , ...currentValue } })
}

handleChangeY(newY) {
  setState((currentValue) => { return { { y: newY } , ...currentValue } })
}

someFunctionality() {
   ... doing something
  handleChange(x) = someComputation()
  handleChange(y) = anotherComputation(x)
}

useEffect

useEffect(()=> {
  let isDisposed = false;
  slowProcess().then(result => {
    if (!isDisposed) { // flag to ensure not to apply older slow processes since a race condition will occur as myState was modified by then
      setMyState(result.json)
    }
  })
  return () => { // this function is called when the dependency 'myState' is mdified thus making the older call irrelevant
    isDisposed = true
  }
}, [myState])

"run once" vs "run once on each remount"

run once (but retriggers on each remount)

function App() {
  // 🔴 Avoid: Effects with logic that should only ever run once
  useEffect(() => {
    loadDataFromLocalStorage();
    checkAuthToken();
  }, []);
  // ...
}

Run once (even on remount)

let didInit = false;

function App() {
  useEffect(() => {
    if (!didInit) {
      didInit = true;
      // ✅ Only runs once per app load
      loadDataFromLocalStorage();
      checkAuthToken();
    }
  }, []);
  // ...
}
⚠️ **GitHub.com Fallback** ⚠️