React 18 Cheatsheet - adonisv79/bytecommander.com GitHub Wiki
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.
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(()=> {
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 (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();
}
}, []);
// ...
}