一、组件的介绍
-
组件表示页面中的部分功能
-
组合多个组件实现完整的页面功能
-
特点
- 复用:当多个页面都需要使用同一个功能的时候,可以抽出一个组件,进行复用
- 独立:每个组件都是互相独立的,保证使用同一个组件的时候不会受到影响
- 组合:有什么功能组合什么组件,就像堆积木一样,组成一个完整功能
二、组件创建方式
1. 函数组件(无状态)
- 使用 JS 的函数(箭头函数)创建的组件
- 约定1:函数名称必须是以 大写字母开头,区分组件和普通 react 元素
- 约定2:函数组件 必须有返回值,返回一个 react 元素,表示该组件的结构
- 渲染函数组件:用函数名作为组件标签名
- 函数组件没有自己的状态,只负责数据展示(静)
// 创建挂载的根节点
const root = ReactDOM.createRoot(document.getElementById("root"))
// 创建函数组件
function Hello() {
return <div>hello Word</div>
}
// 箭头函数方式
const Hello = ()=> <div>hello Word</div>
// 渲染组件
root.render(<Hello />)
2. 类创建组件(有状态)
- 使用 Es6 的 class 创建组件
- 约定1:类名称必须是以 大写字母开头
- 约定2:类组件必须继承 React.Component 父类,从而使用父类中提供的方法或属性
- 约定3:类组件必须提供 render 方法,必须有返回值,返回一个 react 元素,表示该组件的结构
- 渲染类组件:用类名作为组件标签名
- 类组件有自己的状态,负责更新 UI,让页面动起来
// 创建挂载的根节点
const root = ReactDOM.createRoot(document.getElementById("root"))
// 创建类组件
class Hello extends React.Component {
render() {
return <div>hello Word</div>
}
}
// 渲染组件
root.render(<Hello />)
3. 总结
- 数据发生变化,视图也相应的更新,使用 类组件(有状态)
- 页面内容不需要发生变化和用户交互,使用 函数组件(无状态)
三、抽离为独立 JS 文件
- 创建 Hello.js
- 在 Hello.js 中导入 react
- 创建组件(函数 或 类)
- 在 Hello.js 导出该组件
- 在 index.js 中导入 Hello 组件
- 渲染组件
// Hello.js
import React from 'react';
// 创建类组件
class Hello extends React.Component {
render() {
// this.props.age 接收传递的参数
return <div>hello Word,{this.props.age}</div>
}
}
// 导出组件
export default Hello
// index.js
import Hello from './hello';
// 渲染组件
// age='20' 传递给组件的参数
root.render(<Hello age='20' />)
四、状态
1. state
- 状态(state)即为数据,组件内部的 私有数据,只能组件内部使用
- state 的值是对象,表示一个组件中可以有多个数据
- 通过 this.state 获取状态
class Hello extends React.Component {
constructor() {
super()
// 初始化 state
this.state = {
age: 20
}
}
// 简化写法 (推荐)
// state = {
// age: 19
// }
render() {
return <h1>{this.state.age}</h1>
}
}
2. setState
-
状态是可变的
-
语法:this.setState({要修改的值})
-
注意:不能直接修改 state 中的值,这是错误的!!
-
作用:
- 修改 state
- 更新 UI
-
思想:数据驱动视图
class Hello extends React.Component {
// 简化写法
state = {
age: 19,
}
render() {
return (
<div>
<h1>{this.state.age}</h1>
// 错误写法
// <button onClick={() => this.state.age++}>+1</button>
// 正确写法
<button onClick={() => this.setState({ age: this.state.age + 1 })}>
+1
</button>
</div>
)
}
}
3. 无法使用 setState 的问题
- JSX 中掺杂过多的 JS 逻辑代码,会显得非常混乱
- 推荐:将逻辑代码抽离到单独的方法中,保证 JSX 结构清晰
- 报错:Cannot read properties of undefined (reading 'setState')
- 原因:因为 setState 是组件实例中的方法,但是事件处理程序的 this 的值为 undefined
- 希望:this 指向组件实例 (render方法中的this即为组件实例)
1. 方法1 (箭头函数)
class Hello extends React.Component {
// 简化写法
state = {
age: 19,
}
// 方式1
// add() {
// console.log(this);
// this.setState({ age: this.state.age++ })
// }
// 方式2 (推荐)
add = () => {
this.setState({ age: this.state.age++ })
}
render() {
return (
<div>
// 方法1:
// 利用箭头函数自身不绑定 this 的特点
// 箭头函数的 this 指向函数外部 render 方法的 this,再去调用组件实例的 add 方法
// <button onClick={() => this.add()}>+1</button>
// 方法2:
// <button onClick={() => this.add()}>+1</button>
</div>
)
}
}
2. 方法2(bind)
class Hello extends Component {
constructor() {
super()
this.state = {
age: 18,
}
// 使用 bind 改变 this 指向,返回一个新的函数,使用时调用即可
this.add = this.add.bind(this)
}
add() {
console.log(this) // 组件实例
this.setState({ age: this.state.age + 1 })
}
render() {
return (
<div>
<h1>{this.state.age}</h1>
<button onClick={this.add}>+1</button>
</div>
)
}
}
五、事件处理
1. 事件绑定
- react 事件绑定语法与 DOM 事件语法像相似
- 语法:on + 事件名称 = {事件处理程序} ,比如 onClick = {()=>{}}
- 注意:react 事件采用驼峰命名法,比如:onMouseEnter、onFocus
// 类组件
class Hello extends React.Component {
// 事件函数
btn() {
console.log('事件触发');
}
render() {
return (
<div>
hello Word
<br />
<button onClick={this.btn}>点击</button>
</div >
);
}
}
// 函数组件
function Hello() {
// 事件函数
function btn() {
console.log('事件触发');
}
return (
<div> hello Word < br /> <button onClick={btn}>点击</button></div >
);
}
2. 事件对象
- 可以通过 事件处理程序的参数 获取到事件对象
- react 中的事件对象叫做 合成事件(对象)
- 合成事件:兼容所有浏览器,能让 react 正常运行在浏览器中,无需担心跨浏览器兼容性问题
function Hello() {
// 事件函数
function btn(e) {
// 阻住浏览器的默认行为
e.preventDefault()
console.log('事件触发', e);
}
return (
<a href="www.baidu.com" onClick={btn}>跳转页面</a >
);
}
六、表单处理
1. 受控组件(推荐)
- HTML 中表单元素是可输入的,有自己的可变状态
- react 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改
- react 将 state 与表单 value 绑定到了一起,由 state 的值来控制表当元素的值
- 受控组件:其值受到 react 控制的表单元素
实例
class Hello extends React.Component {
state = {
// 1. state 中添加一个状态,做为表单的 value 值(控制表单值的来源)
txt: 18,
}
// 2. 给表单添加 change 事件,将表单的值设为 state 的值(控制表单值的变化)
update = e => {
console.log(e)
this.setState({ txt: e.target.value })
}
render() {
return (
<div>
<h1>{this.state.txt}</h1>
<input name="txt" onChange={this.update} value={this.state.txt} />
</div>
)
}
}
多表单优化
class Hello extends React.Component {
state = {
txt: 18,
check: true,
}
// 多表单优化
update = (e) => {
// 2. 获取 name 属性
const name = e.target.name
// 3. 根据表单类型获取值
const value =
e.target.type === 'checkbox' ? e.target.checked : e.target.value
// 4. 根据 name 找到对应的属性设置 state
this.setState({ [name]: value })
}
render() {
return (
<div>
<h1>{this.state.txt}</h1>
<h1>{this.state.check ? 1 : 0}</h1>
// 1. 给表单添加 name 属性,名称和 state 相同
// 文本框
<input name="txt" onChange={this.update} value={this.state.txt} />
<br />
// 复选框
<input
name="check"
type="checkbox"
onChange={this.update}
checked={this.state.check}
/>
</div>
)
}
}
2. 非可控组件
- ref :获取 DOM 或 组件
- 借助 ref,使用原生 DOM 方式来获取表单元素
class Hello extends React.Component {
constructor() {
super()
// 1. 通过 React.createRef() 创建一个 ref 对象
this.txtRef = React.createRef()
}
getTxt = () => {
// 3. 通过 ref 对象获取文本框的值
console.log('文本框的值:',this.txtRef.current.value)
}
render() {
return (
<div>
// 2. 将创建好的 ref 对象添加到文本框中
<input ref={this.txtRef} onChange={this.update} />
<button onClick={this.getTxt}>获取文本框的值</button>
</div>
)
}
}