跳到主要内容

React 必备 JavaScript 常用语法

React 开发深度依赖现代化 JavaScript(ES6+)特性。掌握这些常用语法不仅能看懂 React 代码,还能在开发组件时写出简洁、优雅、可维护的逻辑。


1. 块级作用域

在 ES6 之前,JavaScript 只有全局作用域 and 函数作用域,使用 var 声明的变量存在变量提升 and 无法限制在块级作用域(如 iffor)中的问题。 ES6 引入了 letconst,它们具有块级作用域

核心规则

  • 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 视为空)。
  • ??(空值合并):只有当左侧为 nullundefined 时,才返回右侧的值。常用于需要保留 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. 可选链操作符

在读取对象深层嵌套的属性时,如果中间某个节点为 nullundefined,传统的 JavaScript 会直接抛出报错导致应用崩溃。可选链操作符可以安全地进行属性读取。

核心规则

  • 如果 ?. 前面的值是 nullundefined,则立即停止求值并返回 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必须使用纯函数,不可使用 splicepush 等修改原数组的方法
展开运算符以不可变方式更新 state(对象/数组)属于浅拷贝。如对象有多层嵌套,需深拷贝或使用 immer
逻辑与 &&控制 JSX 的条件渲染左侧必须是布尔值,若为数字 0 则会在页面上渲染出数字 0
可选链 ?.安全读取 API 返回的深层嵌套数据,防崩溃只能用于读取值,不能用作赋值左值,如 obj?.prop = 1 是非法的
模块化导入引用组件、工具函数、CSS 样式文件默认导入不带大括号 {},命名导入必须带大括号且名称完全一致
async / awaituseEffect 或事件处理中请求后端 APIuseEffect 回调函数本身不能直接写成 async,须在内部声明

掌握了这些现代 JavaScript 特性,你就能在 React 的学习旅程中游刃有余!下一章我们将开始进入 React 独创的 JSX 语法与规范