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
Function | Feature |
---|---|
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
};
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-rendersetState(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 usinginitValue
if ref not element) - Add to element:
<element ref={elRef} />
- Value of ref
- Get:
elRef.current
- Set:
elRef.current = any;
- Get:
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, ...)
- A version of
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
- Using:
useMemo
- Using:
const memorizeValue = useMemo(() => any, [...dependencies])
- Using:
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
-> putref
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);
}