软开(一):React基础
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 的特点:
- 异步执行:调用后不会立刻生效,React 会批量处理
- 自动合并:只需要写要改变的字段,其他字段保持不变
- 触发重渲染:执行后 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;