主题
类组件与函数组件(Day3 2/3)
本页对应 React + TypeScript + 医通项目,共 6 段视频,约 54 分钟。
学习目标
- 能够解释类组件状态数据为什么必须定义在
state上,并通过对比演示证明直接修改普通实例属性不会触发组件重新渲染。 - 能够使用构造函数赋值和类字段直接赋值两种方式定义
state,并说明第二种方式的简洁性。 - 能够判断类组件事件回调中普通方法与箭头函数的
this指向,并使用bind、箭头函数包裹或类字段箭头函数解决this丢失问题。 - 能够使用
setState修改状态数据,并验证修改后会触发render重新执行、页面同步更新。 - 能够在父子类组件之间通过标签属性传递数据,并在子组件中通过
this.props接收、解构和使用外部数据。
知识点
先复习类:类组件的“地基”
为什么需要:类组件的本质就是一个类。如果对“属性定义在哪、方法定义在哪、this 由谁决定”没有感觉,后面 render 里的 this、state、事件回调的 this 都会糊在一起。
怎么想:类里定义属性有两种写法——在 constructor 里写 this.xxx = ...,或者直接在类体里赋值 xxx = ...;定义方法也有两种——普通方法(挂在原型对象上),箭头函数赋值(挂在实例上,this 在“出生”时就绑定好)。
怎么做:转写稿里用一个 Person 类做了对照:
js
class Person {
name = '艾硅谷';
age = 10;
say() { console.log(this.name, this.age); }
eat = () => { console.log(this.name); };
}
const p1 = new Person();
p1.say();
p1.eat();调试过程:再 new 一个 p2,比较 p1.say === p2.say,结果是 true,说明 say 定义在原型对象上,所有实例共用;比较 p1.eat === p2.eat,结果是 false,说明 eat 是每个实例自己的。再把 p1.say 赋给 obj.f1、p1.eat 赋给 obj.f2 去调用:f1 里的 this 变成了 obj(普通方法的 this 由调用者决定);f2 里的 this 仍然是 p1(箭头函数没有自己的 this,它在 constructor 执行时就绑定了实例)。
易错点:不要背“方法里的 this 指向实例”。普通方法的 this 看调用者;箭头函数的 this 看定义位置。
定义类组件的五条规则
为什么需要:JSX 里标签那么多,React 得有一套规则判断“这是组件还是普通 HTML 标签”,类组件本身也得拿到 React 给的能力(state、setState、props 等)。
怎么想:把笔记五条拆成三组——命名(class 定义、首字母大写)、继承(extends React.Component)、渲染(必须有 render,render 返回 jsx)。
怎么做:按笔记逐条落地:
- 本质就是一个类,使用 class 关键字定义;
- 类名首字母必须大写,类名就是组件名;
- 类组件必须继承
React.Component类; - 类组件中必须有 render 方法;
- render 方法中必须(99.99%)返回 React 元素【jsx】。
jsx
class Header extends React.Component{
render(){
// 类组件render方法,永远是当前类组件的实例对象调用的
console.log('header render this: ' ,this);
return (
<div className="header">我是头部</div>
)
}
}易错点:
- 类名小写:
class header在 JSX 里写成<header/>会被当成 HTML 的 header 标签,不是组件; - 忘记 extends:拿不到 React.Component 上的能力;
- render 不返回或返回 undefined:页面什么都不渲染;
- 返回多个并列元素:需要包一层 Fragment 或父元素,笔记代码里
root.render就用了<>...</>。
调用类组件的两种写法
为什么需要:定义完得用起来,JSX 提供单标签和双标签两种形式,和 HTML 习惯一致。
怎么做:笔记里两种都出现了:
jsx
<Header></Header>
<Header/>易错点:单标签必须带 /,写成 <Header> 不闭合会报错;双标签中间本节还没讲 children,先别乱塞内容。
React 调用类组件时做了什么
为什么需要:理解调用过程,才能理解 render 里的 this,以及组件为什么能“自动”执行。
怎么想:JSX 编译后,React 遇到首字母大写的标签,会去查这个标识符是不是组件。
怎么做(笔记三步):
- 发现首字母大写的 jsx 调用标签,会将其当做组件处理;
- 查找是否定义了该组件,如果定义了并且发现是类组件,那么 render 函数会帮我们实例化该类,并用实例化出来的对象调用 render 方法;
- 将 render 方法执行后的返回值,替换掉组件标签调用的位置。
调试过程:在 Header 的 render 里执行 console.log('header render this: ', this),控制台打印出来的是 Header 的实例对象,不是 window,也不是类本身。
易错点:别把“实例化”理解成“执行类”。类本身不执行,执行的是 new 出来的实例的 render。
render 里的 this 永远指向实例
为什么需要:render 里要读 this.state、this.props、调用 this.xxx 方法,如果 this 不是实例,全拿不到。
怎么想:render 不是我们手动调用的,是 React 内部用实例去调 实例.render();调用者是实例,普通方法的 this 由调用者决定,所以 this 就是实例。
怎么做:笔记代码里已经打了这行日志:
jsx
console.log('header render this: ' ,this);易错点:不要把 render 当成需要外部调用的普通工具函数;也不要把 this 存到外部变量再在别处用,事件回调里的 this 是后面单独要处理的话题。
完整代码跑一遍
把笔记代码完整跑起来:
jsx
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 定义Header类组件
class Header extends React.Component{
render(){
// 类组件render方法,永远是当前类组件的实例对象调用的
console.log('header render this: ' ,this);
return (
<div className="header">我是头部</div>
)
}
}
class Main extends React.Component{
render(){
return (
<div className="main">我是内容</div>
)
}
}
class Footer extends React.Component{
render(){
return (
<div className="footer">我是底部</div>
)
}
}
root.render((
<>
<Header></Header>
<Header/>
<Main></Main>
<Footer></Footer>
</>
))root.render 里 Header 出现两次,React 就会实例化两次、调用两次 render,两个实例互不影响——和前面 Person 的 p1、p2 是一个道理。控制台能看到两次 header render this,都是各自的 Header 实例。
易错点:最外层用 Fragment <>...</> 是因为要返回多个兄弟节点;className 不是 class;调用标签首字母大写这条规则贯穿始终。
函数组件本质:一个返回 React 元素的函数
为什么需要:类组件要写 constructor、this、state,入门成本高;函数组件把“组件”还原成更直接的函数,便于理解和组合。怎么想:组件就是“输入数据,返回 UI 描述”的函数,本节先掌握最小模型——函数名就是组件名,返回值就是 React 元素。怎么做:函数名首字母大写,函数体里必须有 return,并且返回 React 元素。易错点:函数名小写会被 JSX 当成原生 HTML 标签;只写 return 但返回字符串、undefined 或多个并列元素,都不是本节要求的 React 元素返回形式。
组件名为什么必须首字母大写
为什么需要:JSX 需要区分“原生标签”和“自定义组件”。怎么想:<div> 是小写原生标签;<Header> 首字母大写,JSX 会把它当变量/组件处理。怎么做:定义 function Header(){},调用时写 <Header/> 或 <Header></Header>,名字保持一致。易错点:定义成 Header 却写成 <header>,React 不会找你的函数组件;组件名和调用标签大小写不一致也会出错。结合转写稿里类组件反复强调的“实例对象、原型、this”等概念来对比:函数组件没有组件实例,不要套用类组件思路去找 this。
返回值必须是 React 元素
为什么需要:render 最终要把组件返回值放到页面上,返回值必须能被 React 渲染。怎么想:先写一个最简结构 return (<div>...</div>);多个同级节点时用 Fragment <>...</> 包裹。怎么做:本节代码中 Header、Main、Footer 都返回一个 <div>;最外层调用时用 <>...</> 包住多个组件标签。易错点:return 后直接写多个并列 <div> 会语法错误;返回 undefined 或忘记 return,组件位置就没有可渲染内容。
调用方式:单标签与对标签
为什么需要:JSX 提供两种等价写法,方便有/无子内容时表达。怎么想:没有子内容用自闭合更简洁;需要写子内容时用对标签。怎么做:单标签调用 <组件名/>,对标签调用 <组件名></组件名>。本节代码故意同时写了 <Header></Header> 和 <Header/>,用来验证两种写法都能调用同一个函数组件。易错点:自闭合标签不能再塞内容;对标签中间的内容会作为 children 处理,本节不展开,但不要误以为“对标签必须写内容”。
调用过程:render 如何把标签替换成返回值
为什么需要:理解 JSX 不是 HTML,组件标签最终要由 React 处理。怎么想:render 遇到首字母大写标签,会查找组件定义;发现是函数组件后,由 render 帮我们调用该函数;函数返回的 React 元素替换掉组件调用标签的位置。怎么做:看下面完整代码,root.render 中写的是组件标签,不是手动 Header()。易错点:不要把手动函数调用和组件调用混为一谈;组件标签应交给 React 渲染流程处理。
jsx
const root = ReactDOM.createRoot(document.querySelector("#root"));
function Header(){
console.log('Header run');
console.log('Header this: ', this);// 函数组件中this 指向undefined
return (
<div>我是Header组件</div>
)
}
function Main(){
return (
<div>我是Main组件</div>
)
}
function Footer(){
return (
<div>我是Footer组件</div>
)
}
root.render((
<>
<Header></Header>
<Header/>
<Main/>
<Footer/>
</>
))用代码观察调用次数与 this
为什么需要:只看定义容易误以为组件只执行一次,实际每次渲染时函数组件都会执行。怎么想:在 Header 里放 console.log('Header run'),可以看到它被调用;代码里写了两个 Header 标签,因此 Header 函数会执行两次,Main、Footer 各一次。怎么做:用 console.log 做最小调试,观察“组件函数是否执行、this 是什么”。易错点:函数组件里打印 this 得到 undefined,这不是 bug,而是提醒你不要在函数组件里依赖 this 访问状态或方法;类组件那套 this.state、this.props 思路不能直接搬过来。转写稿中讨论类组件时出现的“实例对象、原型、bind、undefined、render、props”等术语,在这里要纠正为规范理解:函数组件没有实例,重点看函数参数和返回值。
易错点检查清单
为什么需要:函数组件语法短,错误往往集中在命名和返回值。怎么想:写完先检查“名字大写了吗、return 了吗、返回的是 React 元素吗、标签调用对吗”。怎么做:按清单逐项核对;用 console.log 验证执行次数;多个同级节点用 Fragment 包裹。易错点:组件名小写、忘记 return、返回多个并列元素、在函数组件里找 this、把组件标签写成普通 HTML 标签。掌握这些,就完成了函数组件的最小闭环:定义、调用、由 render 调用并替换标签位置。
先说明本节内容范围
本节笔记标题是“react脚手架构建工程化开发环境”,但转写稿实际围绕类组件的 state、事件回调 this、setState 和 props 展开,没有出现脚手架创建命令。因此下面按转写稿内容整理,不补写未讲到的脚手架步骤。
状态数据为什么必须放在 state 上
为什么需要:组件不仅有 HTML 结构、CSS 样式,还要有“活”的数据来动态渲染;数据变了,页面要跟着更新。
怎么想:如果把数据随便定义成组件实例的普通属性,虽然能读、能改,但改完不会触发组件重新渲染。转写稿用购物车数量举例:数量从 100 改成 0,页面必须重新显示 0,所以数据必须放在特殊位置 state 上。
怎么做:类组件把状态数据定义在 this.state 中;修改时不能直接赋值,而要用 setState。
易错点:把状态写成 this.count = 100 也能改值,但不会触发 render,页面不会更新;不要误以为“值变了页面就一定变”。
state 的两种定义方式
为什么需要:类组件需要初始化状态,写法要清楚且便于维护。
怎么想:state 本质上是类实例上的特殊属性,所以既可以在 constructor 里写,也可以用类字段直接赋值。
怎么做:方式一在构造函数中定义;方式二直接写 state = { count: 100 }。转写稿演示两种方式效果一致,并推荐方式二,因为不用写 constructor、super、this。
jsx
class App extends React.Component {
state = { count: 100, msg: 'hello' }
render() {
return <div>{this.state.count}</div>
}
}易错点:方式二本质上等价于在 constructor 中执行 this.state = {...};不要写成普通变量,也不要在 render 里重新定义状态。
事件回调里的 this 为什么会丢
为什么需要:点击按钮修改状态时,方法里的 this 必须指向当前组件实例,才能访问 this.state 和 this.setState。
怎么想:普通函数的 this 由调用者决定。onClick={this.click1} 只是把函数引用交给事件系统,真正调用时不是组件实例在调用,转写稿调试打印得到 undefined,所以不能直接这样写。
怎么做:可行方案有三种:onClick={this.click2.bind(this)};onClick={() => this.click3()};把方法直接定义成箭头函数 click4 = () => {}。转写稿还演示了在 constructor 中 this.click5 = this.click5.bind(this)。
jsx
click2() { console.log(this) }
click3 = () => { console.log(this) }
<button onClick={this.click2.bind(this)}>click2</button>
<button onClick={() => this.click3()}>click3</button>易错点:直接写 onClick={this.click1} 会丢 this;bind 会返回新函数,不能写成 this.click2.bind(this) 后又在别处误用;直接箭头类字段每个实例都有一份,转写稿从“是否传参、是否占用内存”角度更推荐 bind 或箭头包裹。
setState 才是修改状态并触发渲染的入口
为什么需要:直接改 this.state.count 虽然值会变,但页面不刷新;必须让组件重新 render。
怎么想:转写稿先故意直接赋值,发现数据变成 102、105,但页面没变,render 也没有重新执行;说明需要 React 提供的入口。
怎么做:用 this.setState({ count: this.state.count + num }) 修改;修改 msg 可写 this.setState({ msg: this.state.msg + '!' })。
jsx
addCount = (num) => {
this.setState({ count: this.state.count + num })
}易错点:不要直接 this.state.count += num;setState 接收对象,键名要和状态字段一致;转写稿中即使写入相同值,也会触发 render,但业务上仍应避免无意义更新。
父组件通过 props 给类子组件传数据
为什么需要:多个组件可能使用同一份数据,例如列表组件和底部统计组件都要用同一个数组,需要父子通信。
怎么想:父组件通过子组件标签属性传值;子组件通过固定属性 this.props 接收。
怎么做:父组件写 <ClassCom count={this.state.count} msg={this.state.msg} school="硅谷" />;子组件用 const { count, msg, school } = this.props 解构使用。父组件 setState 后重新 render,子组件收到新的 props 也会重新渲染。
jsx
// 父组件
<ClassCom count={this.state.count} msg={this.state.msg} school="硅谷" />
// 类子组件
const { count, msg, school } = this.props易错点:props 是外部传入数据,不能像 state 那样直接改;标签属性名和子组件接收名要对应,转写稿特意用不同名字说明“传什么属性名,props 里就有什么属性名”。
课堂画面






代码示例
示例一:类组件与函数组件的定义和调用(12-1 / 12-2)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>类组件与函数组件</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"));
// ---------- 类组件 ----------
class Header extends React.Component {
render() {
// 类组件 render 方法,永远是当前类组件的实例对象调用的
console.log('header render this: ', this);
return <div className="header">我是头部</div>;
}
}
class Main extends React.Component {
render() {
return <div className="main">我是内容</div>;
}
}
class Footer extends React.Component {
render() {
return <div className="footer">我是底部</div>;
}
}
// ---------- 函数组件 ----------
function FHeader() {
console.log('FHeader run');
console.log('FHeader this: ', this); // 函数组件中 this 指向 undefined
return <div>我是 Header 函数组件</div>;
}
function FMain() {
return <div>我是 Main 函数组件</div>;
}
function FFooter() {
return <div>我是 Footer 函数组件</div>;
}
root.render(
<>
<Header></Header>
<Header />
<Main></Main>
<Footer></Footer>
<FHeader />
<FMain></FMain>
<FFooter />
</>
);
</script>
</body>
</html>关键行与易错点
class Header extends React.Component:类组件必须继承React.Component,且必须写render(),render里返回 React 元素(JSX)。类名/函数名首字母必须大写:JSX 遇到小写标签会当成原生 HTML 标签处理,只有首字母大写才会被当作组件去查找定义。- 类组件
render中的this永远是当前实例:render是实例调用的,所以实例上的属性都能用this.xxx取到。 - 函数组件里的
this是undefined:函数组件没有实例,不要在里面用this。 <Header></Header>与<Header/>完全等价,两种调用写法都可以。- 调用过程:JSX 发现大写标签 → 若是类组件就实例化并调用
render,若是函数组件就直接调用函数 → 用返回值替换掉标签位置。 - 页面上直接跑 JSX 必须引
babel并且script写type="text/babel",否则浏览器报语法错误。
示例二:state 的定义、事件回调 this 指向、setState 修改状态(12-3 / 12-4 / 12-5)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>state 与 setState</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"));
class App extends React.Component {
// 定义状态方式二:直接赋值(相当于写在 constructor 里 this.state = {...})
state = {
count: 100,
msg: '艾特硅谷'
};
// 定义状态方式一(对照):构造函数中定义
// constructor() {
// super();
// this.state = { count: 100, msg: '艾特硅谷' };
// }
// 箭头函数定义的方法:this 一出生就绑定当前实例
addCount = (num) => {
this.setState({
count: this.state.count + num
});
};
changeMsg = () => {
this.setState({
msg: this.state.msg + '!'
});
};
// 普通方法:this 由调用者决定
click1() {
console.log('click1 this: ', this); // 直接当回调传 → 丢失 this
}
click2() {
console.log('click2 this: ', this); // bind 后 → 当前实例
}
render() {
console.log('App render run');
return (
<div>
<p>count: {this.state.count}</p>
<p>msg: {this.state.msg}</p>
<button onClick={() => this.addCount(3)}>每次 +3</button>
<button onClick={this.changeMsg}>改 msg</button>
<hr />
<button onClick={this.click1}>普通方法(this 丢失)</button>
<button onClick={this.click2.bind(this)}>bind 绑定 this</button>
<button onClick={() => this.click2()}>箭头包裹调用</button>
</div>
);
}
}
root.render(<App />);
</script>
</body>
</html>关键行与易错点
- 状态必须定义在
state上:this.state.count = xxx这种直接赋值虽然能改掉数据,但不会触发组件重新渲染(render不会重新执行),页面永远显示旧值。 - 两种定义方式等价:
state = {...}等价于在constructor里this.state = {...},直接赋值写法更常用、更简单(constructor里必须先super())。 - 修改状态只能用
this.setState({...}):它做两件事——改状态值 + 触发render重新调用,从而页面更新。 - 普通方法的
this由调用者决定:onClick={this.click1}只是把函数引用交出去,事件触发时调用者不是组件实例,this就丢了(严格模式下为undefined)。 bind是「改变 this 指向并返回一个新函数」,不会立即调用,所以能直接写在onClick里;call/apply会立即调用,onClick={this.click2.call(this)}是错的。onClick={() => this.click2()}用的是箭头函数,箭头函数没有自己的 this,沿用render里的 this(即实例),所以也能拿到正确的 this。- 直接在类里
click4 = () => {}也是可行的,但每个实例都会各自复制一份方法,占内存;普通方法写在原型上更省内存。
示例三:父组件向类子组件传值 props(12-6)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>props 父子传值</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 父组件:数据定义在自己身上
class App extends React.Component {
state = {
count: 100,
msg: '艾特硅谷'
};
addCount = () => {
this.setState({
count: this.state.count + 1
});
};
render() {
return (
<div>
<p>父组件 count:{this.state.count}</p>
<button onClick={this.addCount}>count++</button>
{/* 通过子组件标签属性的方式传值:状态数据 + 写死的值都能传 */}
<ClassCom count={this.state.count} msg={this.state.msg} school="艾特硅谷" />
<FuncCom count={this.state.count} msg={this.state.msg} />
</div>
);
}
}
// 类子组件:通过固定的 this.props 接收外部数据
class ClassCom extends React.Component {
render() {
console.log('this.props: ', this.props);
const { count, msg, school } = this.props;
return (
<div>
类子组件:{count} —— {msg} —— {school}
</div>
);
}
}
// 函数子组件:通过形参 props 接收
function FuncCom(props) {
const { count, msg } = props;
return (
<div>
函数子组件:{count} —— {msg}
</div>
);
}
root.render(<App />);
</script>
</body>
</html>关键行与易错点
- 父传子的唯一入口是「子组件标签的属性」:
<ClassCom count={this.state.count} />,属性名自己定,但一般与状态变量名保持一致,避免对不上。 - 类子组件用固定的
this.props接收,this.props是一个对象,键就是父组件写的属性名;函数子组件用形参props接收。 - 传进去的既可以是状态数据(会随
setState变化),也可以是写死的值,比如school="艾特硅谷"。 - 父组件
setState后重新render,子组件也会跟着重新render,此时props拿到的是最新值,所以父子两边的数字会同步变化。 props是只读的外部数据,不要在子组件里修改它;需要自己变化的数据应该定义成子组件自己的state。- 数据多时先解构再使用(
const { count, msg } = this.props),比反复写this.props.count更清晰。
常见错误
现象:点击按钮后
this.state.count变了,但页面不更新。
原因:直接给this.state.xxx赋值,绕过了 React 的更新机制。
修法:改用this.setState({ xxx: 新值 }),需要基于旧值时先读取再计算。现象:事件回调里
this不是组件实例,访问this.state报错或取不到值。
原因:普通方法作为事件回调传递后丢失调用者,this不再指向实例。
修法:用箭头函数包裹调用、类字段箭头函数,或在构造函数中bind(this)。现象:子组件能渲染,但拿不到父组件传的值。
原因:父组件标签属性名与子组件读取的this.props.xxx不一致,或忘记传参。
修法:统一属性名,子组件通过this.props读取;需要多次使用时先解构。现象:把
state定义成普通实例属性,如this.count = 0,修改后视图不响应。
原因:React 只把state作为状态源处理,普通属性变化不会触发重新渲染。
修法:状态统一放到state中,通过setState更新。现象:类组件报 “render is not a function” 或组件不显示。
原因:类组件未继承React.Component、缺少render,或render未返回 React 元素。
修法:补齐继承与render,确保返回 JSX/React 元素;组件名首字母大写。现象:函数组件里写
this.state或试图用this拿数据。
原因:函数组件没有组件实例,this不是数据载体。
修法:函数组件用参数props接收外部数据;需要状态时使用 Hooks,不要照搬类组件写法。
练习
改错题:下面代码点击后数字变了但页面不变,请指出问题并改为正确写法。
jsxadd = () => { this.state.count += 1; };改错题:给按钮绑定
onClick={this.handleClick}后,handleClick中this丢失。请给出至少两种修复方式,并说明推荐场景。组件通信题:父组件有
state = { list: [...] },请把list传给类子组件,并在子组件中解构后渲染长度。判断题:以下写法分别有什么问题?
jsxclass header extends React.Component {} function main() { return <div>main</div>; }要求指出命名、继承、
render、调用方式相关风险。综合题:实现一个类组件:
state中有count,两个按钮分别“+1”和“重置为 0”,要求事件回调中this正确,且视图随状态更新。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 类组件状态数据的理解及为什么一定要定义在state属性上 | 02:18 |
| 2 | 定义状态的方式二 | 01:57 |
| 3 | 类复习 | 12:00 |
| 4 | 类组件事件回调this指向问题 | 16:02 |
| 5 | setState修改状态数据 | 09:07 |
| 6 | 类的子组件如何接收父组件传递的外部数据 | 13:01 |
小结
- 类组件排错先看三件事:是否继承
React.Component、是否有render、render是否返回 React 元素。 - 状态数据只认
state,更新只认setState;直接改state是“值变了、视图不动”的典型根因。 - 事件回调中的
this问题要在绑定阶段解决,不要等到函数体里再猜this。 - 类字段箭头函数写法简洁,但每个实例一份;普通方法在原型上更省内存,选择时结合传参和绑定成本。
props是父传子的单向数据通道,属性名要与子组件读取名一致,子组件不应直接改props。- 组件名首字母大写是 JSX 识别组件的基础规则,函数组件和类组件都一样。
- 多个组件共用同一份数据时,优先把数据提升到共同父级,再通过
props分发。