Skip to main content

React

  • Building User Interface
  • Single-page Application
  • Create Reuseable UI Components (Virtual DOM)
  • To use React in production, you need npm which is included with Node.js.

Creating a React App

Using a build tool

  • Vite has a rich ecosystem of plugins to support fast refresh, JSX, Babel/SWC, and other common features
  • Parcel supports fast refresh, JSX, TypeScript, Flow, and styling out of the box.
  • Rsbuild includes built-in support for React features like fast refresh, JSX, TypeScript, and styling.
npm create vite@latest app-name --template react
# Using TypeScript
npm create vite@latest app-name --template react-ts

# Parcel
npm install --save-dev parcel
# Rsbuild
npx create-rsbuild --template react

Using Full-stack Frameworks

  • Next.js takes full advantage of React’s architecture to enable full-stack React apps.
  • React Router emphasizes standard Web APIs and has several ready to deploy templates for various JavaScript runtimes and platforms.
  • Remix is a full stack web framework that lets you focus on the user interface and work back through web standards to deliver a fast, slick, and resilient user experience.
  • Tanstack Start provides a full-document SSR, streaming, server functions, bundling, and more using tools like Nitro and Vite.
  • RedwoodJS is a full stack React framework with lots of pre-installed packages and configuration that makes it easy to build full-stack web applications.
# Next
npx create-next-app@latest
# React Router
npx create-react-router@latest

Render

public/index.html
<body>
<!-- Your code will put inside root (or any id) div -->
<div id="root"></div>
</body>
src/index.jsx
import ReactDOM from "react-dom/client";

const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
root.render(<p>Hello</p>);

JavaScript XML (JSX)

// `<>` | `<Fragment>` | `<React.Fragment>`
const secretText = "Daniel";
const age = 17;
const users = (
<>
<div>John Doe</div>
<div className="woman">Mary Jane</div>
<input type="text" />
<div>
{1 + 1} {secretText}
</div> // Expression {}
{age >= 18 ? "mature" : "child"} // if statement
</>
);

Components

Class Component

FunctionFeature
contructor(props?) { super(props?); // state configs }
static getDerivedStateFormProps(props, state) { return {} }Call before render (create & update)
render() { return <JSX/> }
componentDidMount() {}Call after 1st render
shouldComponentUpdate() { return boolean; }Set up 1 time
getSnapshotBeforeUpdate(props, state) {}access props & state before update
componentDidUpdate() {}Call after component update
componentWillUnmount() {}Call before component is about to be removed
class Car1 extends React.Component {
constructor(props) {
super(props);
this.state = {
color: "red",
};
}

changeColor = () => this.setState({ color: "blue" });

render() {
return (
<h2>
I am a {this.state.color} {this.props.model}!
</h2>
);
}
}

// Use with props: <Car1 model="Mustang" />

Function Component

// props = { children: null|JSX.Element, attr1: value1, attr2: value2, ..., attrN: valueN }`
function Name(props?) {
return <JSX />;
}

// Usage
<Component attr1={value1} {...otherAttr} />
<Parent {...attrs}><Children /></Parent>
// Event based on HTML Events
// on<EventName>={(event) => {}}
<button type="button" onClick={(e) => {}}>
Click
</button>
// conditions ? <CompIfTrue /> : <CompIfFalse />
function HomePage(user) {
return <div>{user ? <Home /> : <Login />}</div>;
}
function Calendar({ isAllowedButtonControls }) {
return (
<>
{/* Other components */}
{isAllowedButtonControls && <ButtonControls />}
</>
);
}
function ItemList({ items }) {
return (
<>
{/* Each children should have a unique key */}
{items.map((item, index) => {
return <JSX key={index} {...item} />;
})}
</>
);
}
function Car2() {
return <h2>Hi, I am a Car!</h2>;
}
// Arrow function (with default props)
const Garage = ({ showGarage = true }) => {
if (!showGarage) return <Fragment />;
return (
<>
<h1>Who lives in my Garage?</h1>
<Car2 /> {/* Component nested */}
</>
);
};

export default Garage;
// split component to reuse
// Using: import Garage from 'components/Garage';

Feature Component

<Fragment> / <>...</>

  • Wraps multiple elements without adding extra nodes to the DOM.
  • Keeps your UI clean and avoids unexpected wrappers.

<Suspense fallback={<Spinner />}>...</Suspense>

  • Used for components or data that take time to load.

  • Triggers only when using Suspense-aware features, like:

    • 🛠️ Frameworks: Relay, Next.js

    • 📦 Lazy-loaded components:

      const LazyReviewCar = lazy(() => import("./components/cars/ReviewCar.js"));
    • ⏳ React's experimental use() hook to read Promises

<StrictMode><App /></StrictMode>

  • Development-only tool (no effect in production).

  • Helps catch common issues early, like:

    • Deprecated APIs
    • Unexpected side effects
    • Unsafe lifecycle methods

<Profiler id="Component" onRender={onRender}>...</Profiler>

  • Measures performance of a specific component.

  • onRender callback example:

    const onRender = (
    id,
    phase,
    actualDuration,
    baseDuration,
    startTime,
    commitTime
    ) => {
    // Log or analyze performance metrics
    };
  • 📚 View parameter docs

Styles component

  • Using sass/scss: npm i sass
  • Both CSS & SASS can using as file or module
custom.css
.custom-font-weight {
font-weight: bold;
}
.bg-red {
background: #ff0000;
}
Custom.jsx
import React from "react"; // To use JSX
import styleModule from "custom.module.css"; // import as module
import "custom.css"; // using css file

const Custom = ({ name }) => {
const customStyle = {
fontSize: "2rem",
color: "#333333",
};
// css module class will display like [filename]_[classname]__[hash]
return (
<div className="custom-font-weight" style={styleModule["bg-red"]}>
<span style={customStyle}>{"Hello " + name}</span>
</div>
);
};

Hooks

Rules of Hooks
  • Call them at the top level in the body of
    • function component
    • custom Hook
  • Do not call Hooks
    • inside conditions or loops.
    • after a conditional return statement. NOTE HERE FOR NEXT PROJECT
    • in event handlers.
    • in class components.
    • inside functions passed to useMemo, useReducer, or useEffect.
    • inside try/catch/finally blocks.
  • Hooks cannot be conditional
  • import { use<HookName> } from <'react'|'address-of-custom-hooks'>

useState

  • state change after setState call -> component re-render
  • setState(newState) | setState(prevState => { return newState }) (use with callback)
import { useState } from "react";

function Car() {
// const [state, setState] = useState(initState | () => initState);
const [car, setCar] = useState({
brand: "Ford",
model: "Mustang",
year: "1964",
color: "red",
});

const toggleColor = () => {
setCar((previousState) => {
const color = previousState.color === "red" ? "blue" : "red";
return { ...previousState, color };
});
};

return (
<>
<h1>My {car.brand}</h1>
<p>
It is a {car.color} {car.model} from {car.year}.
</p>
<button type="button" onClick={toggleColor}>
Toggle color
</button>
</>
);
}

useDeferredValue

info
  • Debouncing - wait for the user to stop typing (e.g. for a second) before updating the list.
  • Throttling - update the list every once in a while (e.g. at most once a second).
  • useDeferredValue is better suited to optimizing rendering because it is deeply integrated with React itself and adapts to the user’s device.
  • Usage
    • Showing stale content while fresh content is loading
    • Deferring re-rendering for a part of the UI
import { useDeferredValue } from "react";

export default function App() {
const [filter, setFilter] = useState("");
const deferredFitler = useDeferredValue(filter);

return (
<div className="container mt-3">
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
type="text"
className="w-100"
/>
<ListAnimalMap filter={deferredFitler} />
</div>
);
}

useReducer

  • Tracking complex state (like object) -> Same tech like React-Redux
  • Custom handle state
  • Syntax: const [state, dispatch] = useReducer(reducer, initialState);
  • reducer: const reducer = (currentState, action: { type, [any]?: value }) => { return newState }
  • Update: dispatch({ type, [any]?: value })
import { useReducer } from "react";

function reducer(state, action) {
switch (action.type) {
case "incremented_age": {
return {
name: state.name,
age: state.age + 1,
};
}
case "changed_name": {
return {
name: action.nextName,
age: state.age,
};
}
}
throw Error("Unknown action: " + action.type);
}

const initialState = { name: "Taylor", age: 42 };

export default function Form() {
const [state, dispatch] = useReducer(reducer, initialState);

function handleButtonClick() {
dispatch({ type: "incremented_age" });
}

function handleInputChange(e) {
dispatch({
type: "changed_name",
nextName: e.target.value,
});
}

return (
<>
<input value={state.name} onChange={handleInputChange} />
<button onClick={handleButtonClick}>Increment age</button>
<p>
Hello, {state.name}. You are {state.age}.
</p>
</>
);
}

useRef

  • Persist value of component (not affect if component re-render)
  • Used to access a DOM element directly
  • Declare: const elRef = useRef(initValue || undefined); . (Recommend using initValue if ref not element)
  • Add to element: <element ref={elRef} />
  • Value of ref
    • Get: elRef.current
    • Set: elRef.current = any;

useImperativeHandle

  • Customize the handle exposed as a ref
  • useImperativeHandle(ref, createHandle, dependencies?)
  • forwardRef is deprecated in React 19
  • If you can express something as a prop, you should not use a ref.
import React, { useRef, useImperativeHandle } from "react";

const ValidInput = ({ ref, ...props }) => {
const inputRef = useRef();

useImperativeHandle(
ref,
() => ({
addRandomNumber: (max = Number.MAX_SAFE_INTEGER) => {
inputRef.current.value = (Math.random() * max).toFixed(0);
},
}),
[]
);

return <input type="text" ref={inputRef} {...props} />;
};

export default function App() {
const pointingRef = useRef();
const onBtnClick = () =>
pointingRef.current && pointingRef.current.addRandomNumber();

return (
<div>
<ValidInput ref={pointingRef} />
<button type="button" onClick={onBtnClick}>
Random number
</button>
</div>
);
}

useEffect & useLayoutEffect

  • Perform side effect in component
    • fetching data
    • directly update DOM
    • timers
  • Syntax: useEffect(<function>, [...dependencies]?) (React teams recommend)
    • Effect run 1st after component render
    • Effect recall if dependencies has change, none if dependencies is []
  • useLayoutEffect
    • A version of useEffect that fires before the browser repaints the screen.
    • Only use in case need to calculate layout before show UI (tooltips, popover, ...)
import { useState, useEffect } from "react";

function Timer() {
const [count, setCount] = useState(0);

useEffect(() => {
let timer = setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
// clean up function
return () => clearTimeout(timer);
}, [count]); // render when count change

return <h1>I've rendered {count} times!</h1>;
}
Avoid useless fetch when unmount component

Apply for Component which can fetch data when mount to avoid refetch with StrictMode or useEffect has show dependency

<button type="button" onClick={() => setShow(!show)}>
Show modal
</button>;
{
show && <Modal show={show} onHide={() => setShow(false)} />;
}
Modal.tsx
import { useEffect } from "react";
import { createPortal } from "react-dom";

type ModalProps = {
show: boolean;
onHide: () => void;
};

const Modal: React.FC<ModalProps> = ({ show, onHide }) => {
useEffect(() => {
const abortController = new AbortController();
const signal = abortController.signal;

(async () => {
try {
const res = await fetch(
"https://my-json-server.typicode.com/typicode/demo/posts",
{ signal }
);
if (!signal.aborted) {
if (res.ok) {
const data = await res.json();
console.log(data);
} else {
console.log("Status: ", res.status);
}
}
} catch (err) {
if (!signal.aborted) {
console.log(err);
}
}
})();

return () => {
abortController.abort();
};
}, [show]);

return createPortal(
<div className="custom-modal" onClick={onHide}>
Hello
</div>,
document.body
);
};

export default Modal;

useCallback & useMemo

  • Problem: Component re-render when
    • function & value inside (not state) re-render
    • children use props includes that function & value re-render
    • => Need to control re-render when it need with dependencies
  • useCallback
    • Using: const memorizeFunction = useCallback(() => {}, [...dependencies])
    • Avoid child component re-render if function of parent re-render
  • useMemo
    • Using: const memorizeValue = useMemo(() => any, [...dependencies])

useContext

  • Manage state globally
  • State use in component that need it, avoid put parent props -> child props -> ... -> n-child props (prop drilling)
How to use
  • Step 1 - Create
    • const Context = createContext(defaultValue?);
    • Context now is High Order Component
    • defaultValue can put inside to get recommend for state (IDE Support)
  • Step 2 - Wrap
    • <Context.Provider value={{ state1, setState1, state2, sampleArr, ..., any }}>{children}</Context.Provider>
    • All children inside <Context.Provider> can use all props in value
    • value should be an object
  • Step 3 - Get value
    • const { state1, state2 } = useContext(Context);
    • Put Context to useContext to get correct value holder
    • Get only props need to using
import { createContext, use, useContext, useState } from "react";

const UserContext = createContext();

function Component1() {
const [user, setUser] = useState("Jesse Hall");

return (
<UserContext.Provider value={user}>
<h1>{`Hello ${user}!`}</h1>
<Component2 />
</UserContext.Provider>
);
}

function Component2() {
return (
<>
<h1>Component 2</h1>
<Component3 />
</>
);
}

function Component3() {
return (
<>
<h1>Component 3</h1>
<Component4 />
</>
);
}

function Component4() {
// const user = useContext(UserContext);
const user = use(UserContext); // React 19 features

return (
<>
<h1>Component 4</h1>
<h2>{`Hello ${user} again!`}</h2>
</>
);
}

useId

  • const uniqueId = useId();
  • UniqueId will create 1 time for Component when it render
  • Use in case common Component (like Input) reuse more than 2 in parent component -> Avoid dupplicate Id when using
import { useId } from "react";

function PasswordField() {
const passwordHintId = useId();
return (
<>
<label htmlFor={passwordHintId}>Password</label>
<input type="password" id={passwordHintId} />
</>
);
}

export default function App() {
return (
<>
<h2>Choose password</h2>
<PasswordField />
<h2>Confirm password</h2>s
<PasswordField />
</>
);
}

combine tech
  • Suspense + lazy
  • createContext -> Context (Context.Provider value=) -> useContext(Context)
  • useRef -> put ref as props (React 19) -> useImperativeHandle

ReactDOM.createPortal

  • Render some children into a different part of the DOM (drawer, modal, tooltips, ...)
  • createPortal(children, domNode, key?)
import { createPortal } from "react-dom";

export function Modal({ children }) {
return createPortal(children, document.body);
}

Resources

Tools