期末复习

软开(一):React基础

2026 年 06 月 29 日 约 5639 字 · 15 分钟 期末复习

React 类组件基础

本报告基于教材项目(mylab)的实际代码风格,以类组件为核心讲解。


一、类组件的基本结构

React 有两种组件写法:函数组件类组件。本教材主要使用类组件。

1.1 最简单的类组件

import React, { Component } from 'react';

export default class MyPage extends Component {
    render() {
        return (
            <div>Hello World</div>
        );
    }
}
  • import React, { Component } from 'react':必须引入,Component 是所有类组件的基类。
  • export default:导出这个组件,其他文件才能 import 它。
  • class MyPage extends Component:声明一个继承自 Component 的类组件。
  • render()唯一必须实现的方法,返回要显示在页面上的 JSX。

1.2 教材项目的标准写法

教材中大多数页面不直接继承 Component,而是继承封装好的 MyFormComponent

import { MyFormComponent } from '../../api/antdFormMethod.js';

export default class Page1101 extends MyFormComponent {
    // MyFormComponent 继承自 Component
    // 内置了 setFormValues、saveTableRow、deleteTableRow 等便捷方法
}

为什么这样做? MyFormComponent 是教材团队自己封装的基类,里面预置了大量表单操作的通用方法(如保存、删除、表单赋值),子类通过继承可以直接复用,避免重复写代码。


二、state 状态变量

2.1 什么是 state?

state 是组件的内部数据。当 state 发生变化时,React 会自动重新渲染组件,让页面上的内容同步更新。

这是 React 最核心的思想:数据驱动视图

state 数据变化  →  React 自动调用 render()  →  页面更新

2.2 state 的声明方式

export default class MyPage extends MyFormComponent {

    // 写法一:类属性语法(教材常用)
    state = {
        name: '张三',
        age: 18,
        visible: false,
    }

    render() {
        // 读取 state:通过 this.state.xxx
        return <div>{this.state.name}</div>
    }
}

2.3 修改 state:必须用 setState()

永远不要直接修改 state! 直接修改不会触发重新渲染。

// ❌ 错误写法:直接赋值,页面不会更新
this.state.name = '李四';

// ✅ 正确写法:通过 setState 修改
this.setState({ name: '李四' });

setState 的特点:

  1. 异步执行:调用后不会立刻生效,React 会批量处理
  2. 自动合并:只需要写要改变的字段,其他字段保持不变
  3. 触发重渲染:执行后 React 自动重新调用 render()
// setState 的回调函数:在状态更新完成后执行
this.setState({ visible: true }, () => {
    // 这里的代码在 state 更新且页面重新渲染后执行
    console.log('弹窗已打开', this.state.visible); // true
});

2.4 教材中 state 的典型结构

state = {
    // 表格配置(静态配置 + 初始值)
    myTable1: {
        rowindex: 0,             // 当前光标所在行
        keyfield: 'productid',   // 主键字段名
        lastrow: {},             // 修改前的旧数据(用于撤销)
        treefield: 'subcategoryid', // 与左侧树关联的字段
        columns: [               // 表格列定义(偶尔需要动态修改)
            { dataIndex: 'productid', title: '商品编码', width: '80px' },
            { dataIndex: 'productname', title: '商品名称', width: '220px' },
        ],
    },

    // 弹窗控制:false = 关闭,true = 打开
    myWin1: false,

    // 当前操作类型
    addoredit: 'update',  // 'add'=新增,'update'=修改,'query'=查询

    // 当前选中的行数据
    record: {},

    // 可拖动侧边栏宽度(涉及状态变化,必须放 state)
    splitSize: { width: 250 },

    // Tab 页签当前激活的 key
    activetabkey: 'myTab1',
}

为什么 splitSize 放 state 里? 因为用户拖动分隔线时,宽度会变化,变化后需要页面重新渲染来更新 Sider 的宽度。凡是”变化后需要更新页面”的数据,就必须放 state。


三、生命周期方法

类组件有一套完整的生命周期。最常用的是 componentDidMount

3.1 组件生命周期流程

constructor()      → 构造函数,初始化 state
      ↓
render()           → 第一次渲染,生成 DOM
      ↓
componentDidMount() → 组件挂载完成(DOM 已存在),这里做初始化请求
      ↓
(用户操作 → setState → render() 重新执行 → 页面更新)
      ↓
componentWillUnmount() → 组件销毁前,清理定时器等

3.2 componentDidMount 的标准写法

// 写法:箭头函数 + async(教材标准写法)
componentDidMount = async () => {
    // 在这里请求数据库,获取初始数据
    let rs = await reqdoSQL({ sqlprocedure: 'demo1101b' });
    this.setState({ data: rs.rows });
}

为什么用箭头函数?

// 普通方法写法
async componentDidMount() {
    this.setState(...);  // this 有可能丢失!
}

// 箭头函数写法(教材写法)✅
componentDidMount = async () => {
    this.setState(...);  // this 永远指向当前组件,绝对安全
}

箭头函数会自动绑定 this,永远指向当前类的实例,不会丢失。

3.3 async/await 的作用

// 没有 async/await:写法繁琐,回调嵌套
componentDidMount() {
    reqdoSQL({ sqlprocedure: 'demo1101b' }).then(rs => {
        this.setState({ data: rs.rows });
    });
}

// 有 async/await:像写同步代码一样清晰 ✅
componentDidMount = async () => {
    let rs = await reqdoSQL({ sqlprocedure: 'demo1101b' }); // 等数据回来
    this.setState({ data: rs.rows });                         // 再更新页面
}

await 让代码”等待”异步操作完成再继续执行,避免数据还没回来就去用它。


四、render() 与 JSX

4.1 render() 的规则

render() {
    // 1. 可以在 return 之前写 JS 逻辑
    let { columns } = this.state.myTable1;
    let siderwidth = this.state.splitSize.width;
    let isAdmin = this.state.user?.isAdmin;

    // 2. return 里只能有一个根元素
    return (
        // 用 <> </> 空标签包裹多个元素(Fragment)
        <>
            <div>第一个元素</div>
            <div>第二个元素</div>
        </>
    );
}

4.2 JSX 中的 JS 表达式

render() {
    let name = '张三';
    let score = 95;
    let isPass = score >= 60;

    return (
        <div>
            {/* 用 {} 包裹 JS 表达式 */}
            <p>姓名:{name}</p>
            <p>分数:{score}</p>
            <p>是否通过:{isPass ? '通过' : '未通过'}</p>

            {/* 条件渲染 */}
            {isPass && <span style=>恭喜!</span>}

            {/* 列表渲染 */}
            {['语文', '数学', '英语'].map((subject, index) => (
                <li key={index}>{subject}</li>
            ))}
        </div>
    );
}

4.3 JSX 中的样式写法

// 行内样式:用双花括号,属性名用驼峰命名
<div style=>
    文字
</div>

// 注意:CSS 中的 font-size 在 JSX 里写成 fontSize

五、this 关键字

在类组件中,this 始终指向当前类的实例(即当前组件对象)。

5.1 this 的用途

export default class MyPage extends MyFormComponent {
    state = { count: 0 }

    handleClick = () => {
        // this.state:读取当前组件的状态
        let current = this.state.count;

        // this.setState:修改当前组件的状态
        this.setState({ count: current + 1 });

        // this.myTable1:访问当前组件上挂载的子组件引用
        this.myTable1.loadTableData();
    }

    render() {
        return (
            <button onClick={this.handleClick}>
                点击次数:{this.state.count}
            </button>
        );
    }
}

5.2 this 绑定问题(为什么用箭头函数)

export default class MyPage extends Component {

    // ❌ 普通方法:this 可能丢失
    handleClickBad() {
        this.setState({ ... });  // 作为回调传递时 this 会变成 undefined
    }

    // ✅ 箭头函数:this 永远正确
    handleClickGood = () => {
        this.setState({ ... });  // 箭头函数自动绑定,永远安全
    }

    render() {
        return (
            <>
                {/* 普通方法需要手动 .bind(this) */}
                <button onClick={this.handleClickBad.bind(this)}>按钮1</button>

                {/* 箭头函数直接用,不需要 bind */}
                <button onClick={this.handleClickGood}>按钮2</button>
            </>
        );
    }
}

六、全局系统变量 React.sys

教材项目在 src/api/functions.js 中把系统配置挂载到了 React.sys 上:

// functions.js 里(系统启动时执行一次)
React.sys = {
    user: { userid: '...', username: '...' },  // 登录用户信息
    colors: {
        border: '#95B8E7',    // 边框颜色
        toolbar: '#f0f2f5',   // 工具栏背景色
    },
    dateformat: 'YYYY-MM-DD', // 全局日期格式
    rowheight: 32,            // 表格行高
    resizeVHandle: <div style= />,  // 拖拽把手 JSX
}

每个组件文件开头的这两行是标准写法:

const sys = { ...React.sys };  // 复制一份,避免污染全局
const rowheight = 42;           // 表单行高(与表格行高不同)

使用示例:

// 使用系统颜色
<div style=>

// 使用用户信息
<span>{sys.user.username}</span>

七、常用导入语句模板

以下是教材标准页面的完整导入模板:

import React, { Component } from 'react'
import { reqdoSQL, reqdoTree, myLocalTime } from '../../api/functions';
import { focusTreeNode, AntdTree, AntdCascader } from '../../api/antdTrees';
import { MyFormComponent } from '../../api/antdFormMethod.js';
import { Tabs, Button, Form, Layout, Modal, Pagination } from 'antd'
import { AntdInputBox, AntdComboBox, AntdHiddenField, MessageBox } from '../../api/antdClass.js';
import { AntdTable } from '../../api/antdTable.js';
import { Resizable } from 'react-resizable';
import { PaperClipOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';

const { Content, Sider } = Layout;
const sys = { ...React.sys };
const rowheight = 42;