学习 React【Plan - June - Week 1】

发布于:2025-06-07 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、使用 JSX 书写标签语言

JSX 是一种 JavaScript 的语法扩展,React 使用它来描述用户界面。


什么是 JSX?

  • JSX 是 JavaScript 的一种语法扩展。
  • 看起来像 HTML,但它实际上是在 JavaScript 代码中写 XML/HTML。
  • 浏览器并不能直接运行 JSX,需要通过打包工具(如 Babel)将其转译为 JavaScript。

示例:

const element = <h1>Hello, world!</h1>;

1、JSX 的基本规则

使用大写字母定义组件

function MyButton() {
  return <button>I'm a button</button>;
}
  • 小写字母开头的标签,如 <div> 被解析为 HTML 标签。
  • 大写字母开头的标签,如 <MyButton> 被解析为 React 组件。

必须使用闭合标签

  • 所有标签必须闭合(类似 XML 语法)
// 正确
<input />
<br />
<MyComponent />

// 错误
<input>

使用 {} 插入 JavaScript 表达式

const user = "小明";
const element = <h1>Hello, {user}!</h1>;
  • 只能插入表达式(不是语句)

合法表达式:

{1 + 2}
{user.name}
{formatDate(date)}

非法语句:

{if (isTrue) { ... }}
{for (...) { ... }}

使用 className 代替 class

// HTML 写法
<div class="container"></div>

// JSX 写法
<div className="container"></div>

因为 class 是 JavaScript 的关键字,所以要使用 className


使用 camelCase 的属性名

// HTML 写法
<input tabindex="0" onclick="handleClick()" />

// JSX 写法
<input tabIndex={0} onClick={handleClick} />

2、条件渲染和列表渲染

条件渲染

使用三元表达式、逻辑与 &&

{isLoggedIn ? <LogoutButton /> : <LoginButton />}

{messages.length > 0 && <Notification messages={messages} />}

列表渲染

使用 map() 进行循环输出,并为每个子元素设置唯一的 key

const items = ['A', 'B', 'C'];

<ul>
  {items.map(item => <li key={item}>{item}</li>)}
</ul>

3、JSX 转换成 JavaScript 的原理

JSX 会被转译为 React.createElement 调用:

const element = <h1 className="title">Hello</h1>;

// 会被转换为:
const element = React.createElement('h1', { className: 'title' }, 'Hello');

4、组合 JSX

JSX 支持嵌套结构:

function App() {
  return (
    <div>
      <Header />
      <Content />
      <Footer />
    </div>
  );
}

可使用片段(Fragment)避免多余的 DOM 元素:

<>
  <td>内容1</td>
  <td>内容2</td>
</>

5、JSX Tips

注释写法:

{/* 这是注释 */}

多行 JSX 需要用括号包裹:

return (
  <div>
    <h1>Hello</h1>
  </div>
);

二、组件(Component)

React 应用是由组件构成的,组件是可以复用的 UI 单元。


什么是组件?

  • 组件(Component) 是 React 的核心概念。
  • 本质上是一个返回 JSX 的函数。
  • 组件名称必须以大写字母开头。

示例:

function MyButton() {
  return <button>I'm a button</button>;
}

在 JSX 中使用:

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton />
    </div>
  );
}

组件命名规则

  • 必须以大写字母开头,否则会被当成 HTML 标签。
  • 使用 PascalCase 命名约定(每个单词首字母大写)。

1、组件是函数,不是标签

function MyButton() {
  return <button>Click me</button>;
}

这个 MyButton 是一个函数,而 <MyButton /> 是它的使用方式(调用)


2、组件可以复用

你可以多次使用同一个组件,它们是互相独立的:

function MyApp() {
  return (
    <div>
      <MyButton />
      <MyButton />
    </div>
  );
}

每个 <MyButton /> 都会渲染一个独立的按钮。


3、组件的结构建议

建议为每个组件建一个文件(例如 MyButton.jsx),用于项目组织:

src/
├─ components/
│  └─ MyButton.jsx
└─ App.jsx

🧪 示例代码汇总

// MyButton.jsx
export default function MyButton() {
  return <button>I'm a button</button>;
}

// App.jsx
import MyButton from './MyButton';

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

三、State

React 的状态(state)允许组件“记住”信息。状态是让组件有“记忆”的机制,通常用于跟踪用户交互或界面变化。


1、什么是状态(State)?

  • 状态是组件的“记忆”
  • 在每次重新渲染时,组件的状态保持不变
  • 状态的变化会 触发组件的重新渲染

2、如何添加状态?

通过 useState Hook:

import { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
}

useState 解释:

const [state, setState] = useState(initialValue);
名称 含义
state 当前状态值
setState 用于更新状态的函数
initialValue 初始状态值

3、状态的基本用法示例

import { useState } from 'react';

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

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

4、每次点击发生了什么?

  1. 点击按钮时,handleClick 被调用。
  2. setCount(count + 1) 更新状态。
  3. React 重新渲染组件。
  4. 新的 count 显示在界面上。

状态更新不会改变当前值,而是触发一次新的渲染,组件中的 count 会更新为新值。


5、状态在组件之间是隔离的

每个组件实例有自己独立的状态。

<MyButton />
<MyButton />

上面两个按钮互不影响,即使它们使用相同的 useState


6、不要直接修改 state 变量

// 错误写法(不会触发重新渲染)
count = count + 1;

// 正确写法
setCount(count + 1);

只有通过 setCount 这样的更新函数,React 才会触发重新渲染。


7、多个状态变量

可以在一个组件中使用多个 useState

const [count, setCount] = useState(0);
const [name, setName] = useState('React');

8、示例完整代码

import { useState } from 'react';

function MyButton() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

四、响应事件

React 使用类似 HTML 的方式来处理用户交互事件,比如点击、输入、悬停等。但语法略有不同,并支持更强的逻辑功能。


1、事件绑定基础

React 使用 onClickonChange 等属性来绑定事件处理函数。

示例:

function MyButton() {
  function handleClick() {
    alert('你点击了我!');
  }

  return (
    <button onClick={handleClick}>
      点击我
    </button>
  );
}

注意:

  • 使用驼峰命名(如 onClick,而不是 onclick
  • 事件处理函数是一个普通的 JavaScript 函数
  • JSX 中不使用字符串绑定函数(不同于 HTML 的 onclick="handleClick()"

2、为什么使用函数名而不是函数调用?

// 正确
onClick={handleClick}

// 错误(会立即执行)
onClick={handleClick()}

你应传递函数的引用,而不是函数的执行结果。


3、使用箭头函数传参

有时你希望传递参数给事件处理函数,可以使用箭头函数:

function handleClick(name) {
  alert(`Hello, ${name}!`);
}

<button onClick={() => handleClick('小明')}>
  Say Hello
</button>

4、在组件中组合事件处理逻辑

React 鼓励你将组件拆成小块,事件处理函数可以在组件内部定义或向下传递:

function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

function App() {
  function handleClick() {
    alert('Clicked!');
  }

  return (
    <div>
      <Button onClick={handleClick}>按钮1</Button>
      <Button onClick={handleClick}>按钮2</Button>
    </div>
  );
}

5、常见事件类型

React 事件名 对应 HTML 说明
onClick onclick 点击事件
onChange onchange 输入/选择改变
onSubmit onsubmit 表单提交
onMouseEnter onmouseenter 鼠标进入
onKeyDown onkeydown 按键按下

6、阻止默认行为

可以在事件中调用 event.preventDefault()

function handleSubmit(e) {
  e.preventDefault();
  alert('提交已阻止');
}

<form onSubmit={handleSubmit}>
  <button type="submit">提交</button>
</form>

7、React 与原生 DOM 事件的区别

项目 React 原生 HTML
命名方式 驼峰命名,如 onClick 小写,如 onclick
传递方式 传函数引用 传字符串或函数调用
自动阻止冒泡 否,你仍需手动阻止冒泡 同样需手动处理

8、示例完整代码

function Button({ message, children }) {
  function handleClick() {
    alert(message);
  }

  return (
    <button onClick={handleClick}>
      {children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <Button message="你好!">点我</Button>
      <Button message="再见!">再点我</Button>
    </div>
  );
}

五、React 哲学

“React 哲学” 教你如何从 UI 设计图开始,一步步将页面拆解为组件,再构建出数据驱动的交互式界面。


示例场景简介

我们要实现一个可搜索的商品表格(Searchable Product Table),它包含:

  • 一个搜索框
  • 一个是否只显示有库存商品的勾选框
  • 一个根据品类分组的商品表格

构建步骤总览

React 官方建议采用 五步法

  1. 将 UI 拆解为组件层级结构
  2. 构建组件的静态版本(无交互)
  3. 确定最小但完整的 UI 状态表示
  4. 确定哪些组件拥有状态(状态提升)
  5. 添加反向数据流(处理用户输入)

1、第一步:将 UI 拆解为组件层级

观察 UI,并根据界面结构拆出以下组件:

组件层级结构

FilterableProductTable (父组件)
├─ SearchBar
└─ ProductTable
   ├─ ProductCategoryRow
   └─ ProductRow

每个组件的职责

组件名 作用描述
FilterableProductTable 管理所有状态,整合其他组件
SearchBar 输入搜索文本与是否过滤库存
ProductTable 接收数据与过滤条件,渲染表格
ProductCategoryRow 显示每个品类的标题
ProductRow 显示单个商品

2、第二步:构建静态版本(无交互)

  • 使用父组件 FilterableProductTable 将假数据通过 props 传给子组件。
  • 每个组件只关注如何显示数据,不包含状态或交互。
  • 假数据示例:
const PRODUCTS = [
  {category: "Fruits", price: "$1", stocked: true, name: "Apple"},
  {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
  {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
  {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
  {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
  {category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];

3、第三步:确定最小但完整的 UI 状态(State)

根据界面交互功能,确定需要驱动 UI 的状态

  • 搜索文本(filterText
  • 是否只显示有库存商品(inStockOnly

不是状态的内容(可由 props 或其他状态推导得出):

  • 商品数据(是静态的)
  • 分类标题(可从数据中提取)
  • 筛选后的商品列表(由 filterText + inStockOnly 计算)

4、第四步:决定状态的归属

状态应该放在最“靠上的共同祖先组件”中。

状态名 所属组件 原因
filterText FilterableProductTable SearchBar 和 ProductTable 都使用它
inStockOnly FilterableProductTable 同上

5、第五步:添加反向数据流(提升状态 + 子传父)

SearchBar 接收 filterTextinStockOnly 作为 props,并通过 onChange 回调将用户输入传递给父组件修改状态。

function SearchBar({ filterText, inStockOnly, onFilterTextChange, onInStockChange }) {
  return (
    <form>
      <input
        type="text"
        value={filterText}
        onChange={(e) => onFilterTextChange(e.target.value)}
        placeholder="Search..."
      />
      <label>
        <input
          type="checkbox"
          checked={inStockOnly}
          onChange={(e) => onInStockChange(e.target.checked)}
        />
        Only show products in stock
      </label>
    </form>
  );
}

6、组件结构(最终)

<FilterableProductTable products={PRODUCTS} />

// 内部包含
  └── <SearchBar
         filterText={...}
         inStockOnly={...}
         onFilterTextChange={...}
         onInStockChange={...}
       />

  └── <ProductTable
         products={...}
         filterText={...}
         inStockOnly={...}
       />

网站公告

今日签到

点亮在社区的每一天
去签到