React 必备 JavaScript 常用语法
React 开发深度依赖现代化 JavaScript(ES6+)特性。掌握这些常用语法不仅能看懂 React 代码,还能在开发组件时写出简洁、优雅、可维护的逻辑。
1. 块级作用域
在 ES6 之前,JavaScript 只有全局作用域 and 函数作用域,使用 var 声明的变量存在变量提升 and 无法限制在块级作用域(如 if 或 for)中的问题。
ES6 引入了 let 和 const,它们具有块级作用域。
核心规则
const(首选):声明一个只读的常量。一旦声明,其值就不能重新赋值。如果声明的是对象或数组,其内部的属性或元素是可以被修改的,但不能指向全新的对象或数组。let:声明一个可以被重新赋值的变量。仅在变量值确实需要改变时才使用。
React 实战案例
import { useState } from 'react';
function Counter() {
// 1. 状态和更新函数:使用 const 声明,因为我们不会直接给 count/setCount 重新赋值
const [count, setCount] = useState(0);
const handleIncrement = () => {
// 2. 局部变量:使用 let,因为需要在逻辑中重新赋值
let step = 1;
if (count >= 10) {
step = 2; // 根据条件动态修改步长
}
setCount(prev => prev + step);
};
return <button onClick={handleIncrement}>增加 {count}</button>;
}
2. 箭头函数
箭头函数提供了一种更简洁的函数书写方式,并且它没有自己的 this,其 this 值在定义时就被绑定到了外部词法环境。
核心规则
- 隐式返回:如果函数体只有单行表达式,可以省略大括号
{}和return关键字。 - 词法
this:不创建自己的this上下文。
React 实战案例
// 1. 事件处理函数与隐式返回
const ItemList = ({ items }: { items: string[] }) => (
// 隐式返回 JSX 结构
<ul>
{/* 2. 在 map 中使用箭头函数隐式返回 <li> 元素 */}
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
// 3. 普通写法 vs 隐式返回写法
// 普通写法
const add = (a: number, b: number) => {
return a + b;
};
// 隐式返回写法
const addImplicit = (a: number, b: number) => a + b;
3. 解构赋值
解构赋值允许我们从数组或对象中快速提取值并赋值给变量,使代码更加清晰。
核心规则
- 对象解构:通过属性名匹配来提取值,支持重命名和设置默认值。
- 数组解构:通过索引位置匹配来提取值。
React 实战案例
import { useState } from 'react';
interface UserProps {
name: string;
age?: number; // 可选属性
theme?: string;
}
// 1. 接收 Props 时进行对象解构,并设置默认值 age = 18
function UserProfile({ name, age = 18, theme: activeTheme = 'light' }: UserProps) {
// theme 被重命名为 activeTheme
return (
<div className={`profile-${activeTheme}`}>
<p>姓名: {name}</p>
<p>年龄: {age}</p>
</div>
);
}
// 2. 数组解构:useState 的返回值就是数组,[当前值, 更新函数]
const [status, setStatus] = useState('idle');
4. 模板字符串
模板字符串使用反引号(`)包裹,允许在字符串中嵌入变量或表达式,并且支持多行文本。
核心规则
- 使用
${expression}进行变量或表达式插入。
React 实战案例
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger';
size: 'sm' | 'md' | 'lg';
isActive: boolean;
}
function Button({ variant, size, isActive }: ButtonProps) {
// 动态拼接 CSS 类名
const buttonClass = `btn btn-${variant} btn-size-${size} ${isActive ? 'btn-active' : ''}`;
return <button className={buttonClass}>点击我</button>;
}
5. 数组常用方法
React 中的 UI 渲染非常依赖纯函数数组方法,特别是 map 用于列表渲染,filter 用于数据过滤。
核心方法
map:遍历数组并返回一个新数组,常用于把数据数组转换成 JSX 元素数组。filter:过滤出符合条件的元素,返回一个新数组,常用于删除列表中的某项。find:找到第一个符合条件的元素并返回,找不到返回undefined。reduce:累计数组的值,返回单个计算结果。
React 实战案例
interface Todo {
id: number;
text: string;
completed: boolean;
}
function TodoList({ todos }: { todos: Todo[] }) {
// 1. 使用 filter 过滤出未完成的待办事项
const activeTodos = todos.filter(todo => !todo.completed);
// 2. 使用 reduce 计算未完成的数量
const activeCount = todos.reduce((acc, todo) => acc + (todo.completed ? 0 : 1), 0);
// 3. 使用 find 查找特定 ID 的任务
const urgentTodo = todos.find(todo => todo.id === 999);
return (
<div>
<p>未完成任务数: {activeCount}</p>
{urgentTodo && <p>加急任务: {urgentTodo.text}</p>}
<ul>
{/* 4. 使用 map 将数据渲染为列表组件 */}
{activeTodos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
{todo.text}
</li>
))}
</ul>
</div>
);
}
6. 展开与剩余运算符
使用三个点(...)表示,具有不同的作用场景。
核心规则
- 展开运算符(Spread):用于将数组或对象解构并展开到另一个数组或对象中。在 React 中,我们必须保持 State 的不可变性,通常使用展开运算符来复制并更新对象或数组。
- 剩余参数(Rest):用于收集函数剩余的参数或对象解构中未提取的属性。
React 实战案例
import { useState } from 'react';
// 1. 展开运算符更新对象 State
const [user, setUser] = useState({ name: 'Alice', age: 20 });
const updateAge = () => {
// ❌ 错误做法:直接修改原对象,React 无法检测到引用变化,不会触发重渲染
// user.age = 21;
// setUser(user);
// ✅ 正确做法:利用展开运算符复制原对象属性,并覆盖被修改的属性
setUser(prevUser => ({
...prevUser,
age: 21
}));
};
// 2. 展开运算符更新数组 State
const [list, setList] = useState([1, 2, 3]);
const addItem = () => {
// ✅ 正确做法:创建新数组,展开旧元素并在末尾追加新元素
setList(prevList => [...prevList, 4]);
};
// 3. 剩余参数 (Rest) 在 React Props 中的应用
function Input({ label, ...restProps }: { label: string; [key: string]: any }) {
// label 从 props 中被提取出单独使用
// restProps 收集了剩余的所有属性(如 type, placeholder, onChange 等)
return (
<label>
{label}
{/* 展开属性:一次性将所有属性传递给 input 元素 */}
<input {...restProps} />
</label>
);
}
7. 逻辑运算符与空值合并
逻辑运算符在 React 的条件渲染和默认值设定中发挥着至关重要的作用。
核心规则
&&(逻辑与):短路运算符。如果左侧为真,返回右侧的值;如果左侧为假,直接返回左侧的值。常用于条件渲染。||(逻辑或):如果左侧为真值,返回左侧;否则返回右侧值。常用于提供宽泛的默认值(但会将0、""、false视为空)。??(空值合并):只有当左侧为null或undefined时,才返回右侧的值。常用于需要保留0或空字符串""作为有效值的场景。
React 实战案例
interface AlertProps {
message: string;
showIcon?: boolean;
errorCode?: number;
}
function Alert({ message, showIcon = true, errorCode }: AlertProps) {
// 1. 逻辑或 (||) 提供默认值(注意:如果 errorCode 是 0,0 || 500 会返回 500)
const code = errorCode || 500;
// 2. 空值合并 (??) 安全地保留 0(如果 errorCode 是 0,0 ?? 500 会返回 0)
const safeCode = errorCode ?? 500;
return (
<div className="alert">
{/* 3. 短路与 (&&) 条件渲染:只有当 showIcon 为 true 时才渲染 <span> */}
{showIcon && <span className="icon">⚠️</span>}
<p>{message} (错误码: {safeCode})</p>
</div>
);
}
[!WARNING] 在使用
&&进行条件渲染时,如果左侧表达式的结果是数字0,React 会把0渲染到页面上!解决方案:确保左侧表达式是布尔值,例如:
{count > 0 && <p>有数据</p>}而不是{count && <p>有数据</p>}。
8. 可选链操作符
在读取对象深层嵌套的属性时,如果中间某个节点为 null 或 undefined,传统的 JavaScript 会直接抛出报错导致应用崩溃。可选链操作符可以安全地进行属性读取。
核心规则
- 如果
?.前面的值是null或undefined,则立即停止求值并返回undefined,而不会报错。
React 实战案例
interface ApiData {
user?: {
profile?: {
avatarUrl?: string;
};
};
}
function Avatar({ data }: { data: ApiData }) {
// 1. 安全地访问深层嵌套属性
// 如果 data.user 缺失,不会报错,而是安全地返回 undefined
const avatar = data.user?.profile?.avatarUrl;
// 2. 结合空值合并运算符设定备用头像
const displayAvatar = data.user?.profile?.avatarUrl ?? '/default-avatar.png';
// 3. 安全地调用可能不存在的方法
// data.onLoad?.() // 仅当 onLoad 方法存在时才执行
return <img src={displayAvatar} alt="用户头像" />;
}
9. 模块化
ES6 模块化是现代 Web 拼装组件的基础。
核心规则
- 命名导出与导入:一个文件可以有多个命名导出,导入时名字必须对应(或者使用
as重命名),且需要加花括号{}。 - 默认导出与导入:一个文件只能有一个默认导出,导入时可以自定义任何命名,不需要加花括号
{}。
React 实战案例
// 1. 命名导出 utils.ts
export const add = (a: number, b: number) => a + b;
export const subtract = (a: number, b: number) => a - b;
import { useState } from 'react'; // 导入第三方库的命名导出
// 2. 默认导出组件 Button.tsx
export default function Button() {
return <button>确定</button>;
}
import React from 'react'; // 导入默认值
import MyButton from './Button'; // 导入默认导出,名字可以自定义 (如 MyButton)
import { add, subtract as sub } from './utils'; // 导入多个命名导出,并支持重命名
10. 异步编程与数据请求
在 React 中,我们经常需要处理异步操作,最常见的是在组件挂载时或用户交互时向后端 API 发送请求。
核心规则
async关键字声明一个异步函数,该函数总是返回一个Promise。await关键字只能在async函数中使用,用于等待一个Promise解决并获取其结果。
React 实战案例
import { useEffect, useState } from 'react';
interface User {
id: number;
name: string;
}
function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// 在 useEffect 中使用 async/await 发起网络请求
useEffect(() => {
// useEffect 的回调函数本身不能是 async 的,所以要在内部声明一个 async 函数并调用它
const fetchUsers = async () => {
try {
setLoading(true);
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error('网络响应异常');
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err instanceof Error ? err.message : '获取数据失败');
} finally {
setLoading(false);
}
};
fetchUsers();
}, []); // 仅在组件挂载时运行
if (loading) return <p>加载中...</p>;
if (error) return <p>出错了: {error}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
总结速查表
| 语法特性 | React 经典应用场景 | 关键避坑指南 |
|---|---|---|
| let / const | 声明组件内局部变量、Hooks 的返回值 | 优先使用 const;绝不能直接修改 useState 解构出来的变量值 |
| 箭头函数 | 事件处理器、数组 map 映射 JSX 元素 | 多行逻辑不能用隐式返回,需写明 { return ... } |
| 解构赋值 | 获取 props、提取 useState 和自定义 Hook 结果 | 重命名时需注意属性的作用域冲突,可设定合理的默认值 |
| 模板字符串 | 动态拼接 className(类名)或内联样式 | 反引号(`)不要写成单引号 |
| 数组方法 | 列表渲染(map)、过滤条件项(filter) | 必须使用纯函数,不可使用 splice、push 等修改原数组的方法 |
| 展开运算符 | 以不可变方式更新 state(对象/数组) | 属于浅拷贝。如对象有多层嵌套,需深拷贝或使用 immer 库 |
逻辑与 && | 控制 JSX 的条件渲染 | 左侧必须是布尔值,若为数字 0 则会在页面上渲染出数字 0 |
可选链 ?. | 安全读取 API 返回的深层嵌套数据,防崩溃 | 只能用于读取值,不能用作赋值左值,如 obj?.prop = 1 是非法的 |
| 模块化导入 | 引用组件、工具函数、CSS 样式文件 | 默认导入不带大括号 {},命名导入必须带大括号且名称完全一致 |
| async / await | 在 useEffect 或事件处理中请求后端 API | useEffect 回调函数本身不能直接写成 async,须在内部声明 |
掌握了这些现代 JavaScript 特性,你就能在 React 的学习旅程中游刃有余!下一章我们将开始进入 React 独创的 JSX 语法与规范。