React 常用语法指南:从 JSX 到 Hooks

前言

React 的“常用语法”本质上就是一组稳定的写法:用 JSX 描述 UI,用组件拆分页面,用 props/state 驱动渲染,用 Hook 管理状态与副作用。

下面用一份“速查 + 最小示例”的方式,把日常开发里最常见的写法汇总起来。

JSX 要点

  1. 表达式嵌入:用 {} 包住变量或表达式
1
2
3
4
const name = "Alice";
export default function Demo() {
return <div>你好,{name}</div>;
}
  1. 属性命名:HTML 里的 class 在 JSX 中写成 className
1
2
3
export default function Demo() {
return <div className="card">内容</div>;
}
  1. 条件/循环通常不写在 JSX 内部“复杂逻辑”,尽量提前算好或用表达式包裹

样式绑定

React 里最常见的两种样式绑定是:style(内联)和 className(类名)。

内联 style

style 接收一个对象,字段名用驼峰命名(例如 fontSize):

1
2
3
4
5
6
7
8
9
export default function InlineStyleDemo({ color }) {
const size = 16;

return (
<p style={{ color, fontSize: size, margin: 0 }}>
这是一段内联样式
</p>
);
}

要点:

  • style 对象可以是动态的(依赖 props/state
  • 数值通常是像素值(例如 fontSize: 16

className 条件

当样式需要根据状态切换时,常用模式是“条件表达式拼接类名”:

1
2
3
4
5
export default function ClassNameDemo({ ok }) {
const cls = ok ? "btn btn-ok" : "btn btn-bad";

return <button className={cls}>{ok ? "通过" : "失败"}</button>;
}

也可以用模板字符串:

1
2
3
4
5
6
7
export default function ClassNameDemo({ active }) {
return (
<div className={`card ${active ? "card--active" : ""}`}>
状态样式
</div>
);
}

组件写法

最常见的是函数组件(Function Component):

1
2
3
export default function Button({ text, onClick }) {
return <button onClick={onClick}>{text}</button>;
}

组件可以复用,也可以组合多个子组件形成页面结构。

Props 使用

props 是“父组件传给子组件的数据 + 回调”。常见写法:

  1. 解构 props 参数
1
2
3
4
5
6
7
8
function Card({ title, children }) {
return (
<section>
<h3>{title}</h3>
<div>{children}</div>
</section>
);
}
  1. 传回调:让子组件“通知”父组件发生了什么
1
2
3
4
5
6
7
function App() {
function handleSave() {
console.log("保存");
}

return <button onClick={handleSave}>保存</button>;
}

State(useState)

useState 声明状态,并用更新函数修改状态:

1
2
3
4
5
6
7
8
9
10
11
12
import { useState } from "react";

export default function Counter() {
const [count, setCount] = useState(0);

return (
<div>
<p>当前:{count}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
</div>
);
}

要点:

  • 直接修改状态值(如 count++)是错误的
  • 若更新依赖旧值,建议用函数式更新:setX((prev) => ...)

多状态

最常见的做法是:在同一个组件里调用多个 useState,分别管理互不影响的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { useState } from "react";

export default function MultiStateDemo() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const [loading, setLoading] = useState(false);

async function handleSend() {
setLoading(true);
try {
// 模拟请求
await new Promise((r) => setTimeout(r, 500));
console.log("发送:", text);
setCount((c) => c + 1);
setText("");
} finally {
setLoading(false);
}
}

return (
<div style={{ padding: 12 }}>
<p>count:{count}</p>

<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="输入内容"
style={{ marginRight: 8 }}
/>

<button onClick={handleSend} disabled={loading}>
{loading ? "发送中..." : "发送"}
</button>
</div>
);
}

对象 state(可选)

如果多个状态属于同一个“表单/数据块”,也可以把它们放到一个对象里管理(更新时用展开运算符合并)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { useState } from "react";

export default function FormObjectDemo() {
const [form, setForm] = useState({ name: "", age: 0 });

return (
<div>
<input
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
placeholder="姓名"
/>
<input
type="number"
value={form.age}
onChange={(e) => setForm({ ...form, age: Number(e.target.value) })}
placeholder="年龄"
/>
<p>
{form.name}({form.age})
</p>
</div>
);
}

事件处理

在 JSX 中监听事件:onClickonChange 等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { useState } from "react";

export default function FormDemo() {
const [text, setText] = useState("");

return (
<div>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="输入点内容"
/>
<p>你输入:{text}</p>
</div>
);
}

条件渲染

常见三种写法:

  1. if 提前返回
1
2
3
4
export default function Status({ ok }) {
if (!ok) return <p>失败</p>;
return <p>成功</p>;
}
  1. 三元运算符
1
2
3
export default function Status({ ok }) {
return <p>{ok ? "成功" : "失败"}</p>;
}
  1. &&(适合短内容)
1
2
3
export default function Tips({ show }) {
return <div>{show && <p>提示:这是一个小弹窗</p>}</div>;
}

列表渲染

当你把数组变成一组元素,通常用 map,并给每一项一个稳定的 key

1
2
3
4
5
6
7
8
9
export default function List({ items }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}

要点:

  • key 应该使用“业务稳定且唯一”的值(例如数据库 id)
  • 不要用索引当 key(除非列表绝不会重排且短期场景可控)

useEffect(副作用)

副作用例子:请求接口、订阅事件、读取本地存储、设置定时器等。

  1. 组件首次渲染后执行一次:依赖数组 []
1
2
3
4
5
6
7
8
9
10
11
12
13
import { useEffect, useState } from "react";

export default function User() {
const [name, setName] = useState("");

useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((r) => r.json())
.then((data) => setName(data.name));
}, []);

return <p>用户名:{name}</p>;
}
  1. 依赖变化时重新执行:依赖数组放变量
1
2
3
useEffect(() => {
console.log("id 变化:", id);
}, [id]);
  1. 清理函数:返回一个函数,适合取消订阅/清理定时器
1
2
3
4
useEffect(() => {
const timer = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(timer);
}, []);

Hooks

除了 useState/useEffect,日常还常用这些 Hook:

useRef

用于保存“不会触发渲染的值”或“DOM 引用”(比如直接聚焦输入框):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { useRef } from "react";

export default function FocusInput() {
const inputRef = useRef(null);

function focus() {
inputRef.current?.focus();
}

return (
<div>
<input ref={inputRef} placeholder="点按钮聚焦" />
<button onClick={focus}>聚焦</button>
</div>
);
}

useContext

用于在组件树中“免传多层 props”共享数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createContext, useContext, useState } from "react";

const ThemeContext = createContext("light");

function ThemeLabel() {
const theme = useContext(ThemeContext);
return <p>当前主题:{theme}</p>;
}

export default function ThemeProviderDemo() {
const [theme, setTheme] = useState("light");

return (
<ThemeContext.Provider value={theme}>
<ThemeLabel />
<button onClick={() => setTheme("dark")}>切换到 dark</button>
</ThemeContext.Provider>
);
}

useMemo

用于缓存“计算结果”,避免每次渲染都重复计算(适合昂贵计算):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useMemo, useState } from "react";

export default function ExpensiveCalc() {
const [n, setN] = useState(1);

const result = useMemo(() => {
// 模拟昂贵计算
let sum = 0;
for (let i = 0; i < 100000; i++) sum += i;
return sum + n;
}, [n]);

return (
<div>
<p>结果:{result}</p>
<button onClick={() => setN((x) => x + 1)}>n+1</button>
</div>
);
}

useCallback

用于缓存“函数引用”,常用于把回调传给子组件以减少不必要的渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useCallback, useState } from "react";

function Child({ onAction }) {
return <button onClick={onAction}>执行</button>;
}

export default function Parent() {
const [count, setCount] = useState(0);

const onAction = useCallback(() => {
setCount((c) => c + 1);
}, []);

return (
<div>
<p>count:{count}</p>
<Child onAction={onAction} />
</div>
);
}

useReducer

适合更复杂的状态逻辑:用“reducer(state, action) 更新”替代一堆 setState:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { useReducer } from "react";

function reducer(state, action) {
switch (action.type) {
case "inc":
return { count: state.count + 1 };
case "reset":
return { count: 0 };
default:
return state;
}
}

export default function ReducerDemo() {
const [state, dispatch] = useReducer(reducer, { count: 0 });

return (
<div>
<p>count:{state.count}</p>
<button onClick={() => dispatch({ type: "inc" })}>+1</button>
<button onClick={() => dispatch({ type: "reset" })}>重置</button>
</div>
);
}

自定义 Hook

把重复逻辑封装成 useXxx,在内部自由使用 Hook,再把结果 return 给组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useState } from "react";

function useCounter(initial = 0) {
const [count, setCount] = useState(initial);
const inc = () => setCount((c) => c + 1);
return { count, inc };
}

export default function Demo() {
const { count, inc } = useCounter(0);
return (
<div>
<p>count:{count}</p>
<button onClick={inc}>+1</button>
</div>
);
}

规则提醒

  • Hook 必须在组件或自定义 Hook 的最外层调用:不要写在条件/循环里
  • useEffect 的依赖数组要尽量写对,否则会导致重复执行或数据不同步

常见最佳实践

  • 状态更新优先使用“不可变”思维:复制数组/对象再更新
  • 尽量让组件职责单一:UI 组件只负责展示,逻辑可以拆到自定义 Hook 或上层组件
  • 将“重复逻辑”抽成函数/组件,避免在多个地方复制粘贴
  • 让 key 保持稳定,减少不必要的 DOM 重建

快速小抄

  • JSX:{表达式}className
  • 组件:函数组件 export default function X() {}
  • props:子组件接收,父组件传入数据/回调
  • state:const [x, setX] = useState(...)
  • 事件:onClick/onChange + 事件参数 e
  • 条件:if / 三元 / &&
  • 列表:items.map(...) + 稳定 key
  • 副作用:useEffect(() => {...}, deps) + 清理 return () => ...