Basic React
新建react项目
npm create vite@latest my-react-app
cd my-react-app
npm install
npm run dev

spread and destruturing array or object
- spread array
const arr = [1, 2, 3];
const copy = [...arr];
console.log(copy); // [1, 2, 3]
- spread object
const obj = { a: 1, b: 2 };
const copy = { ...obj };
console.log(copy); // { a: 1, b: 2 }
- array destruturing
const [a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20
- object destruturing
const person = { name: "Alice", age: 25 };
const { name, age } = person;
console.log(name, age); // Alice 25
Updating a nested object
consider the object construct like this,
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
you can update immutable object like this way,
const nextArtwork = { ...person.artwork, city: 'New Delhi' };
const nextPerson = { ...person, artwork: nextArtwork };
setPerson(nextPerson);
or
setPerson({
...person, // Copy other fields
artwork: { // but replace the artwork
...person.artwork, // with the same one
city: 'New Delhi' // but in New Delhi!
}
});
or use useImmer instead of useState
another way you can use
use-immerlibrary, handle deeper nested object.
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
箭头函数
| 写法 | 含义 | 是否需要写 return |
|---|---|---|
(param) => ( expression ) | 隐式返回,自动返回括号里的表达式结果 | ❌ 不需要 |
(param) => { statement } | 函数体语句块,不会自动返回,需要手动写 return | ✅ 需要 |
react数据遍历指定key
key 只能用在 父组件 map 渲染列表的元素上,而不是子组件内部。
export default function Contact({
img,
imgAlt,
name,
phoneNumber,
email,
}: ContactInfo) {
return (
//这是 不对的,因为 key 只能用在 父组件 map
// 渲染列表的元素上,而不是子组件内部。
// <article className="contact-card" key={id}>
<article className="contact-card">
<img src={img} alt={imgAlt} />
<h3>{name}</h3>
<div className="info-group">
<img src={phoneIcon} alt="phone icon" />
<p>{phoneNumber}</p>
</div>
<div className="info-group">
<img src={mailIcon} alt="mail icon" />
<p>{email}</p>
</div>
</article>
);
}
function App() {
return (
<div className="contacts">
{contacts.map((contact) => (
<Contact key={contact.id} {...contact} />
))}
</div>
);
}
向react组件传递参数
import phoneIcon from "./images/phone-icon.png";
import mailIcon from "./images/mail-icon.png";
import type ContactInfo from "./ContactInfo";
export default function Contact({
img,
imgAlt,
name,
phoneNumber,
email,
}: ContactInfo) {
return (
//这是 不对的,因为 key 只能用在 父组件 map 渲染列表的元素上,而不是子组件内部。
// <article className="contact-card" key={id}>
<article className="contact-card">
<img src={img} alt={imgAlt} />
<h3>{name}</h3>
<div className="info-group">
<img src={phoneIcon} alt="phone icon" />
<p>{phoneNumber}</p>
</div>
<div className="info-group">
<img src={mailIcon} alt="mail icon" />
<p>{email}</p>
</div>
</article>
);
}
import "./index.css";
import Contact from "./Contact";
import { contacts } from "./ContactInfo";
function App() {
return (
<div className="contacts">
{contacts.map((contact) => (
<Contact key={contact.id} {...contact} />
))}
</div>
);
}
export default App;
useState function
import { useState } from "react";
export default function Main() {
// Array destructuring
const [ingredients, setIngredients] = useState<string[]>([
"Chicken",
"Oregano",
"Tomatoes",
]);
const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
// the key is the name of input
const newIngredient = formData.get("ingredient");
if (typeof newIngredient === "string" && newIngredient?.trim() !== "") {
setIngredients((prev) => [...prev, newIngredient]);
} else {
console.warn("No ingredient provided");
}
};
return (
<main>
<form className="add-ingredient-form" onSubmit={handleSubmit}>
<input
type="text"
name="ingredient"
aria-label="Add ingredient"
placeholder="e.g. oregano"
></input>
<button>Add ingredient</button>
</form>
<h1>Ingrredients on hand:</h1>
<ul className="ingredient-ul">
{ingredients.map((ingredient) => (
<li key={ingredient}>{ingredient}</li>
))}
</ul>
</main>
);
}
onClick传值函数名or匿名函数
Version 1 (onClick={getRecipe})
You’re giving React a function reference. React will call getRecipe() only when the button is clicked.
This is the simplest and most efficient form — no new function is created on every render.
Version 2 (onClick={() => getRecipe()})
You’re creating a new anonymous function on each render that calls getRecipe() inside it.
It still works — React will call your wrapper function on click, which in turn calls getRecipe() — but it’s one extra layer of indirection and (very slightly) less efficient.
| Code | When to use | Notes |
|---|---|---|
onClick={changeToHeld} | Default, most common,or closure function | Pass function directly |
onClick={() => changeToHeld(id)} | When you need to wrap logic or pass arguments | Creates a new function every render |
pass parameter function
import React, { useState } from "react";
import "./index.css";
import Die from "./Die";
import { nanoid } from "nanoid/non-secure";
export interface DiceProp {
value: number;
isHeld: boolean;
id: string;
}
const App: React.FC = () => {
const generateDice = () =>
Array.from({ length: 10 }, () => ({
value: Math.ceil(Math.random() * 6),
isHeld: false,
id: nanoid(),
}));
const [dice, setDice] = useState<DiceProp[]>(generateDice());
const changeToHeld = (id: string) =>
setDice((prevDice) =>
prevDice.map((die) => (die.id === id ? { ...die, isHeld: true } : die))
);
const diceElements = dice.map((diceProp) => (
<Die key={diceProp.id} changeToHeld={changeToHeld} die={diceProp} />
));
const rollDice = () => {
setDice(generateDice());
};
return (
<main>
<div className="dice-container">{diceElements}</div>
<button className="roll-button" onClick={rollDice}>
Roll
</button>
</main>
);
};
export default App;
import type { DiceProp } from "./App";
/**
* Recommendation
* For scalable apps / larger state management: Option 1 is preferred
* because it separates state (data) from behavior (actions), which aligns with React best practices.
*/
const Die: React.FC<{ die: DiceProp; changeToHeld: (id: string) => void }> = ({
die,
changeToHeld,
}) => {
const { value, isHeld, id } = die;
return (
<button
className={isHeld ? "held" : "notHeld"}
onClick={() => changeToHeld(id)}
>
{value}
</button>
);
};
export default Die;
pass a closure function
import React, { useState } from "react";
import "./index.css";
import Die from "./Die";
import { nanoid } from "nanoid/non-secure";
export interface DiceProp {
value: number;
isHeld: boolean;
id: string;
}
const App: React.FC = () => {
const generateDice = () =>
Array.from({ length: 10 }, () => ({
value: Math.ceil(Math.random() * 6),
isHeld: false,
id: nanoid(),
}));
const [dice, setDice] = useState<DiceProp[]>(generateDice());
const changeToHeld = (id: string) =>
setDice((prevDice) =>
prevDice.map((die) => (die.id === id ? { ...die, isHeld: true } : die))
);
const diceElements = dice.map((diceProp) => (
<Die
key={diceProp.id}
// closure function
changeToHeld={() => changeToHeld(diceProp.id)}
die={diceProp}
/>
));
const rollDice = () => {
setDice(generateDice());
};
return (
<main>
<div className="dice-container">{diceElements}</div>
<button className="roll-button" onClick={rollDice}>
Roll
</button>
</main>
);
};
export default App;
import type { DiceProp } from "./App";
const Die: React.FC<{ die: DiceProp; changeToHeld: () => void }> = ({
die,
changeToHeld,
}) => {
const { value, isHeld } = die;
return (
<button className={isHeld ? "held" : "notHeld"} onClick={changeToHeld}>
{value}
</button>
);
};
export default Die;
如何动态设置变量名为key值
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.currentTarget;
setMeme((prev) => ({ ...prev, name: value }));
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.currentTarget;
setMeme((prev) => ({ ...prev, [name]: value }));
};
tsx能渲染的类型
React 的 JSX 表达式 { ... } 里,只能直接渲染以下类型:
- 字符串 (string)
- 数字 (number)
- 布尔值 (boolean)
- React 元素 (JSX 元素)
- 数组(包含以上类型)
- 不能渲染对象,对象数组
useEffect function
when calling useEffect, depends on the dependents in [].
That's look this example.
import React from "react";
export default function App() {
const [starWarsData, setStarWarsData] = React.useState({});
const [count, setCount] = React.useState(0);
console.log("Rendered!");
React.useEffect(() => {
console.log("useEffect runs");
fetch("https://swapi.dev/api/people/1")
.then((res) => res.json())
.then((data) => setStarWarsData(data)
);
}, [starWarsData]);
return (
<div>
<h2>The count is {count}</h2>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>
Add
</button>
<pre>{JSON.stringify(starWarsData, null, 2)}</pre>
</div>
);
}
Each time setStarWarsData(data) runs, React receives a new { ... } object.
Even if the JSON content is identical, it’s a different object reference,
so React thinks the dependency changed and re-runs the effect — causing an infinite loop.
corret usage
React.useEffect(() => {
console.log("useEffect runs");
fetch("https://swapi.dev/api/people/1")
.then((res) => res.json())
.then((data) =>
setStarWarsData(data)
);
}, []);
What happens when visit the page first time?
Rendered!
App.tsx:11 useEffect runs
App.tsx:8 Rendered!
| Stage | Trigger | What happens | Console output |
|---|---|---|---|
| 1 | Initial mount | Component function executes | Rendered! |
| 2 | After mount | useEffect callback runs | useEffect runs |
| 3 | State updated | Component re-renders with new data | Rendered! |
useRef
import { useRef } from "react";
export default function FocusInput() {
const inputRef = useRef<HTMLInputElement>(null);
const handleFocus = () => {
// current 可能为 null,因此需要进行非空检查
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Click the button to focus me" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
how to clean resouce in useEffect
import { useEffect, useState } from "react";
export default function WindowTracker() {
const [windowWidth, setWindownWidth] = useState<number>(window.innerWidth);
useEffect(() => {
const watchWindowWidth = () => {
console.log("resized!");
setWindownWidth(window.innerWidth);
};
window.addEventListener("resize", watchWindowWidth);
// cleaning up what creating before
return () => {
window.removeEventListener("resize", watchWindowWidth);
console.log("cleaning up");
};
}, []);
return <h1>Window width: {windowWidth}</h1>;
}
Array.from
// The _ means: “Ignore the element, just use the index.”
Array.from({ length: 3 }, (_, i) => i * 2);
// → [0, 2, 4]
当没有参数需要被使用时,使用不写参数.
Array.from({ length: 10 }, () => Math.ceil(Math.random() * 6))
Array.every
array.every(callback(element, index, array), thisArg?)
- with callback only
const threshold = { limit: 10 };
const numbers = [3, 5, 9];
// Arrow functions do NOT bind `this`, so `thisArg` is ignored.
const allBelowLimit = numbers.every((num) => num < threshold.limit);
console.log(allBelowLimit); // true
- with callback and thisArg
const threshold = 5;
const numbers = [6, 7, 8, 9, 10];
const allAboveThreshold = numbers.every(function (num) {
return num > this.threshold;
}, { threshold });
console.log(allAboveThreshold); // true
lazy Initialization for useState
1️⃣ Lazy initialization: useState(() => generateDice())
- Here, you pass a function to useState.
- React will call that function once — only on the initial render.
- The return value of the function becomes the initial state.
- After that, React never calls the function again, because it already has the state stored internally.
- Benefit: Useful when computing the initial state is expensive.
Explain:
- Component mounts → React calls () => generateDice().
- The return value (DiceProp[]) is stored as the state.
- Component re-renders → React reuses the stored state, function is never called again.
2️⃣ Non-lazy way:
const [dice, setDice] = useState(generateDice());
- Here, you call generateDice() immediately and pass its return value to useState.
- This happens every render of the component, but only the first value matters for initialization.
Explain:
- Component renders → generateDice() runs → returns array → React stores as state.
- Component re-renders later → generateDice() still runs, but React ignores it, because the state is already managed internally.
- Problem: If generateDice() is expensive, you waste CPU calling it unnecessarily on every render.
- 署名:在原有代码和衍生代码中,保留原作者署名及代码来源信息。
- 保留许可证:在原有代码和衍生代码中,保留Apache 2.0协议文件。
- 署名:应在使用本文档的全部或部分内容时候,注明原作者及来源信息。
- 非商业性使用:不得用于商业出版或其他任何带有商业性质的行为。如需商业使用,请联系作者。
- 相同方式共享的条件:在本文档基础上演绎、修改的作品,应当继续以知识共享署名 4.0国际许可协议进行许可。