06-15 1818人
React 组件介绍
组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。
组件表示页面中的部分功能,组合多个组件实现完整的页面。
功能特点:可复用、独立、可组合。
React组件的两种创建方式
React 创建组件方法:
- 使用函数
function
- 使用类
class
函数组件
1)什么是函数组件?
- 使用 JS 函数(普通,箭头)创建的组件。
2)定义函数组件
- 语法约定
- 函数名称
首字母必需大写
,React 据此来区分组件和 HTML 元素。 - 函数
必须有返回值
,表示该组件的 UI 结构,如果不渲染任何内容可返回null
。
- 函数名称
// 普通函数
function Header() {
return <div>头部组件</div>;
}
// 箭头函数
const Footer = () => {
return <div>底部组件</div>;
};
// 不渲染内容
const Loading = () => {
const loading = false;
return loading ? <div>正在加载...</div> : null;
};
3)使用组件
- 函数的名称就是组件名称,使用组件就是把组件名称当标签使用即可。
- 组件标签可以是单标签也可以是双标签。
import ReactDom from 'react-dom';
// 普通函数
function Header() {
return <div>头部组件</div>;
}
// 箭头函数
const Footer = () => {
return <div>底部组件</div>;
};
// 加载组件,不渲染内容
const Loading = () => {
const loading = false;
return loading ? <div>正在加载...</div> : null;
};
// 根组件
const App = () => {
return (
<>
<Header />
<Loading />
<Footer />
</>
);
};
ReactDom.render(<App />, document.getElementById('root'));
总结
- 创建函数组件,首字母大写,需要返回值,不渲染就返回
null
。 - 使用函数组件,组件名称当作标签使用即可。
类组件
class 语法
复习一下定义class、定义属性、定义函数。
// 动物
class Animal {
address = '地球';
eat() {
console.log('吃');
}
}
extends
继承父类
// 猫
class Cat extends Animal {
run() {
console.log('跑');
}
}
const cat = new Cat();
cat.run(); // 跑
cat.eat(); // 吃
console.log(cat.address); // 地球
总结: class
创建类,extends
继承类,可以使用父类的属性和函数。
1)什么是类组件?
- 使用
class
语法创建的组件就是类组件
2)定义类组件
- 约定:类名首字母必需大写
- 约定:必须继承
React.Component
父类 - 约定:必需有
render
函数,返回 UI 结构,无渲染可返回 null
import { Component } from 'react';
class Header extends Component {
render() {
return <div>头部组件</div>;
}
}
3)使用类组件
- 类名称就是组件名称,使用组件就是把组件名称当标签使用即可。
import { Component } from 'react';
import ReactDom from 'react-dom';
// 头部
class Header extends Component {
render() {
return <div>头部组件</div>;
}
}
// 根组件
class App extends Component {
render() {
return (
<>
<Header />
</>
);
}
}
ReactDom.render(<App />, document.getElementById('root'));
总结
- 使用
class
定义类,使用extends
继承React.Component
完成类组件定义 - 类名
首字母大写
,必须有render
函数返回 UI 结构,无渲染可返回null
- 使用的时候把类名当作
标签
使用即可
组件抽离
如果所有组件写在一个文件,代码写在一起后续会难以维护,组件作为一个独立的个体,一般都会放到一个单独的JS文件中。
抽离组件
- 定义一个
js
或者jsx
文件定义组件默认导出 - 使用组件导入即可,当作标签使用。
具体操作:
1.新建 src/components/Header.jsx
类组件,新建 src/components/Footer.jsx
函数组件
import { Component } from 'react';
class Header extends Component {
render() {
return <div>头部组件</div>;
}
}
export default Header;
const Footer = () => {
return <div>底部组件</div>;
};
export default Footer;
2.新建 src/App.jsx
组件, 导入Header
Footer
组件使用。
import { Component } from 'react';
import Header from './components/Header.jsx';
import Footer from './components/Footer.jsx';
class App extends Component {
render() {
return (
<>
<Header />
内容
<Footer />
</>
);
}
}
3.index.js
使用 App
根组件
import ReactDom from 'react-dom';
import App from './App.jsx';
ReactDom.render(<App />, document.getElementById('root'));
无状态组件和有状态组件
简单理解:
无状态(函数)组件,负责静态结构展示
有状态(类)组件,负责更新UI,让页面动起来
1.无状态组件
- 组件本身不定义状态,没有组件生命周期,只负责 UI 渲染。
React16.8
之前的函数组件都是无状态组件,Hooks
出现后函数组件也可以有状态。
2.有状态组件
- 组件本身有独立数据,拥有组件生命周期,存在交互行为。
class
组件可以定义组件自己的状态,拥有组件生命周期,它是有状态组件。
3.它们的区别
- 无状态组件由于没有维护状态只做渲染,性能较好。有状态组件提供数据和生命周期,能力更强。
4.如何去选择
React16.8
之前,组件不需要维护数据只渲染就使用函数组件
,有数据和交互使用类组件
。你需要去判断,有心智负担。React16.8
之后,Hooks
出现给函数提供状态,建议使用函数组件即可。
总结
- 组件本身没有状态就是无状态组件,组件本身提供状态就是有状态组件。
- 16.8 之前,无状态组件使用函数组件,有状态组件使用类组件。16.8 之后,统一可使用函数组件。
- React 没有说完全取代类组件,老项目中还是类组件居多,我们有必要学习下它的具体用法。
类组件 - 定义状态
- 定义
state
属性定义组件状态,属于组件自己的数据,它的值是个对象。 - 使用
state
的时候通过this
去访问即可,例如:this.state.xxx
。 - 数据发生变化,驱动视图更新。
import { Component } from 'react';
class App extends Component {
// 状态
state = {
title: '数码产品',
list: ['电脑', '手机', '相机'],
};
render() {
return (
<>
<h3>{this.state.title}</h3>
<ul>
{this.state.list.map((item) => {
return <li key={item}>{item}</li>;
})}
</ul>
</>
);
}
}
export default App;
总结:
- 定义
state
属性,值是对象存储数据,this.state.xxx
使用数据,数据驱动视图更新。
类组件 - 绑定事件
- 在类中声明事件处理函数,在标签上使用
on+事件名称={处理函数}
的方式绑定事件,事件名称需要遵循大驼峰
规则。 - 处理函数默认的参数就是事件对象,可以使用事件对象处理默认行为和事件冒泡。
import { Component } from 'react';
class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
// 默认行为
e.preventDefault();
// 事件冒泡
e.stopPropagation();
console.log('handleClick');
}
handleMouseEnter() {
console.log('handleMouseEnter');
}
render() {
return (
<>
<div onMouseEnter={this.handleMouseEnter}>
计数器:{this.state.count}
</div>
<div>
<a href="http://www.iyouhun.com" onClick={this.handleClick}>
按钮
</a>
</div>
</>
);
}
}
export default App;
总结:
- 绑定事件的方式和原生的方式一致,使用
on+事件名称={处理函数}
方式绑定 - 事件名称使用
大驼峰
规则,例如:onClick
onMouseEnter
, 处理函数默认传参为事件对象。
事件绑定this指向
- 在事件处理函数中打印
this.state.count
发现报错,this
是个undefined
。 - 演示函数调用对
this
指向的影响,得出函数谁调用this
就执行谁。 - 找出原因:处理函数不是通过组件去调用的,导致出现
this
不是组件问题。
1.发现this是undefined
import { Component } from 'react';
class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
console.log(e);
// Uncaught TypeError: Cannot read properties of undefined (reading 'state')
console.log(this.state.count);
}
render() {
return (
<>
<div>计数器:{this.state.count}</div>
<div>
<button onClick={this.handleClick}>按钮</button>
</div>
</>
);
}
}
export default App;
2.演示处理函数调用对 this 的影响
const obj = {
name: 'tom',
say() {
console.log(this);
},
};
obj.say(); // 打印:{name: 'tom', say: function}
const say = obj.say;
say(); // 打印:window对象 严格模式
3.问题原因
- 类组件声明的处理函数,赋值给
on+事件名称
属性,调用的时候不是通过组件调用的。
处理 this 指向问题
- 通过绑定箭头函数解决 this 问题
- 通过 bind 解决 this 问题
- 通过声明箭头函数解决 this 问题
1.通过绑定箭头函数解决 this 问题
import { Component } from "react";
class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
console.log(e)
console.log(this.state.count)
}
render() {
return (
<>
<div>计数器:{this.state.count}</div>
<div>
+ <button onClick={(e)=>this.handleClick(e)}>按钮</button>
</div>
</>
);
}
}
export default App;
2.通过 bind 解决 this 问题
import { Component } from "react";
class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
console.log(e)
console.log(this.state.count)
}
render() {
return (
<>
<div>计数器:{this.state.count}</div>
<div>
+ <button onClick={this.handleClick.bind(this)}>按钮</button>
</div>
</>
);
}
}
export default App;
3.通过声明箭头函数解决 this 问题(推荐)
利用箭头函数形式的class实例方法。
注意:该语法是实验性语法,但是,由于babel的存在可以直接使用。
import { Component } from "react";
class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
+ handleClick = (e) => {
console.log(e)
console.log(this.state.count)
}
render() {
return (
<>
<div>计数器:{this.state.count}</div>
<div>
<button onClick={this.handleClick}>按钮</button>
</div>
</>
);
}
}
export default App;
类组件 - setState 使用
- React 类组件提供一个函数
setState({需修改数据})
,可以更新数据和视图。 - 直接修改 state 中的数据是不会更新视图,演示简单数据,数组,对象的正确修改方式。
1.通过setState的来修改数据更新视图
import { Component } from 'react';
class App extends Component {
state = {
count: 0,
};
handleClick = () => {
// 修改数据
this.setState({
// key是要修改的数据名称,value是对应的新值
count: this.state.count + 1,
});
};
render() {
return (
<>
<div>计数器:{this.state.count}</div>
<div>
<button onClick={this.handleClick}>按钮</button>
</div>
</>
);
}
}
export default App;
2.修改数组和修改对象的正确姿势
import { Component } from 'react';
class App extends Component {
state = {
count: 0,
user: {
name: 'jack',
age: 18,
},
list: ['电脑', '手机'],
};
handleClick = () => {
// 修改数据
this.setState({
// key是要修改的数据名称,value是对应的新值
count: this.state.count + 1,
});
};
updateList = () => {
// 修改列表
this.setState({
list: [...this.state.list, '相机'],
});
};
updateUser = () => {
// 修改对象
this.setState({
user: {
...this.state.user,
name: 'tony',
},
});
};
render() {
return (
<>
<div>计数器:{this.state.count}</div>
<div>
<button onClick={this.handleClick}>按钮</button>
</div>
<hr />
<div>商品:{this.state.list.join(',')}</div>
<button onClick={this.updateList}>改数组</button>
<hr />
<div>
姓名:{this.state.user.name},年龄:{this.state.user.age}
</div>
<button onClick={this.updateUser}>改对象</button>
</>
);
}
}
export default App;
类组件 - 受控组件
1.什么是受控组件
表单元素的值被 React 中
state
控制,这个表单元素就是受控组件。2.如何绑定表单元素,如:
input:text
input:checkbox
import { Component } from 'react';
class App extends Component {
state = {
mobile: '13811112222',
isAgree: true,
};
changeMobile = (e) => {
this.setState({
mobile: e.target.value,
});
};
changeAgree = (e) => {
this.setState({
isAgree: e.target.checked,
});
};
render() {
return (
<>
<div>
<input
value={this.state.mobile}
onChange={this.changeMobile}
type="text"
placeholder="请输入手机号"
/>
</div>
<div>
<input
checked={this.state.isAgree}
onChange={this.changeAgree}
type="checkbox"
/>
同意用户协议和隐私条款
</div>
</>
);
}
}
export default App;
总结:
- 使用
state
的数据赋值给表单原生,通过onChange
监听值改变修改 state 数据,完成表单元素的绑定。 - 这种表单元素称为受控组件。
类组件 - 非受控组件
1.什么是非受控组件?
- 没有通过 state 控制的表单元素,它自己控制自身的值,就是非受控组件
2.通过 ref 获取表单元素获取非受控组件的值
import { Component, createRef } from 'react';
class App extends Component {
// 获取非受控组件的值
// 1. 通过createRef创建一个ref对象
// 2. 给元素绑定ref属性值为创建的ref对象
// 3. 通过ref对象的current获取元素,再获取它的值
mobileRef = createRef();
getMobile = () => {
console.log(this.mobileRef.current.value);
};
render() {
return (
<>
<div>
{/* 没有被state控制的表单原生认为是非受控组件 */}
<input ref={this.mobileRef} type="text" placeholder="请输入手机号" />
<button onClick={this.getMobile}>获取</button>
</div>
</>
);
}
}
export default App;
总结:
ref
的作用:获取DOM或组件- 借助于
ref
,使用原生 DOM方式来获取表单元素值。
总结
1.组件的两种创建方式:函数组件和类组件
2.无状态(函数)组件,负责静态结构展示
3.有状态(类)组件,负责更新UI,让页面动起来
4.绑定事件注意this指向问题
5.推荐使用受控组件来处理表单
6.完全利用JS语言的能力创建组件,这是 React的思想
欢迎留言