三、React组件基础
目标
◆ 能够使用函数创建组件
◆ 能够使用class创建组件
◆ 能够给React元素绑定事件
◆ 能够使用state和setState()
◆ 能够处理事件中的this指向问题
◆ 能够使用受控组件方式处理表单
目录
◆ React 组件介绍
◆ React 组件的两种创建方式
◆ React 事件处理
◆ 有状态组件和无状态组件
◆ 组件中的state和setState()
◆ 事件绑定 this指向
◆ 表单处理
1.React组件介绍
● 组件是React的一等公民,使用React就是在用组件
● 组件表示顽面中的部分功能
● 组合多个组件实现完整的页面功能
● 特点:可复用、独立、可组合
2. React组件 的两种创建方式
2.1 使用函数创建组件
● 函数组件:使用JS的函数(或箭头函数)创建的组件
● 约定1: 函数名称必须以大写字母开头
● 约定2 :函数组件必须有返回值,表示该组件的结构
● 如果返回值为 null ,表示不渲染任何内容
function Hello(){
return(
<div>我是Hello组件</div>
)
}
// 箭头函数
// const Hello = () =><div>我是箭头函数创建的Hello组件</div>
ReactDOM.render(<Hello/>,document.getElementById('root'))
小结:
● 使用JS中的函数创建的组件叫做 :函数组件
● 函数组件必须有返回值
● 组件名称必须以大写字母开头,React据此区分组件和普通的React元素
● 使用函数名作为组件标签名
2.2 使用类创建组件
● 类组件:使用ES6的class创建的组件
● 约定1 :类名称也必须以大写字母开头
● 约定2 :类组件应该继承React.Component
父类,从而可以使用父类中提供的方法或属性
● 约定3 :类组件必须提供render()
方法
● 约定4 : render()方法必须有返回值,表示该组件的结构
class Hello extends React.Component{
render() {
return(
<div>我是类组件</div>
)
}
}
ReactDOM.render(<Hello/>,document.getElementById('root'))
2.3 抽离为独立JS文件
● 思考:项目中的组件多了之后,该如何组织这些组件呢?
● 选择一: 将所有组件放在同一个JS文件中
● 选择二 :将每个组件放到单独的JS文件中
●组件作为一 个独立的个体,一般都会放到一个单独的JS文件中
步骤:
-
创建Hello.js
-
在Hello.js中导入React
-
创建组件( 函数 或 类 )
-
在Hello.js中导出该组件
-
在index.js中导入Hello组件
-
渲染组件
// Hello.js
import React from 'react'
class Hello extends React.Component{
render(){
return(
<div>我是独立的一个组件</div>
)
}
}
export default Hello
// index.js
import Hello from './Hello';
ReactDOM.render(<Hello/>,document.getElementById('root'))
3. React事件处理
3.1 事件绑定
● React 事件绑定语法与DOM事件语法相似
● 语法: on+事件名称= {事件处理程序} ,比如: onClick={()=> {}}
● 注意: React事件采用驼峰命名法,比如: onMouseEnter、onFocus
class App extends React.Component{
handleClick(){
console.log('单击');
}
render(){
return(
<button onClick={this.handleClick}>绑定事件处理--单机事件</button>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
● 在函数组件中绑定事件:
function App() {
function handleClick() {
console.log('单击事件触发了')
}
return (
<button onClick={handleClick}>点我</button>
)
}
ReactDOM.render(<App/>,document.getElementById('root'))
3.2 事件对象
● 可以通过事件处理程序的参数获取到事件对象
● React 中的事件对象叫做:合成事件(对象)
●合成事件 :兼容所有浏览器、无需担心跨浏览器兼容性问题
class App extends React.Component{
handleClick(e){
// 阻止浏览器默认行为
e.preventDefault()
console.log('a标签被触发了');
}
render(){
return(
<a href="https://www.baidu.com/" onClick={this.handleClick}>事件对象</a>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
4. 有状态组件和无状态组件
● 函数组件又叫做无状态组件,类组件又叫做有状态组件
● 状态( state )即数据
● 函数组件没有自己的状态,只负责数据展示(静)
● 类组件有自己的状态,负责更新UI,页面“动”起来
5. 组件中的state和setState
5.1 state的基本使用
● 状态( state )即数据,是组件内部的私有数据,只能在组件内部使用
● state 的值是对象,表示一个组件中可以有多个数据
● 获取状态: this.state
class App extends React.Component{
// 初始state
state= {
count:10
}
render(){
return(
// 获取数据
<h1>计数器:{this.state.count}</h1>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
5.2 setState()修改状态
● 状态是可变的
● 语法: this.setState({要修改的数据})
● 注意:不要直接修改state中的值,这是错误的!
● setState()作用: 1.修改state 2.更新UI
● 思想:数据驱动视图
class App extends React.Component{
// 初始state
state = {
count:10
}
render(){
return(
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={()=>{
this.setState({
count:this.state.count+1
})
}}>+1</button>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
5.3 从JSX中抽离事件处理程序
● JSX 中掺杂过多JS逻辑代码,会显得非常混乱
● 推荐: 将逻辑抽离到单独的方法中,保证JSX结构清晰
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xPDEtQBh-1632039956750)(C:\Users\十\AppData\Roaming\Typora\typora-user-images\image-20210806092743767.png)]
● 原因: 事件处理程序中this的值为undefined
● 希望: this指向组件实例( render方法中的this即为组件实例)
6. 事件绑定this指向
1. 箭头函数
● 利用箭头函数自身不绑定this的特点
onIncrement () {
this.setState({
count:this.state.count+1
})
}
//render()
<button onClick={ () => this.onIncrement () }></button>
2. Function.prototype.bind()
● 利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起
constructor(){
super()
this.state= {
count:10
}
this.onIncrement = this.onIncrement.bind(this)
}
onIncrement () {
this.setState({
count:this.state.count+1
})
}
//render()
<button onClick={this.onIncrement}>+1</button>
*3. class 的实例方法 (推荐使用)
● 利用箭头函数形式的class实例方法(类)
● 注意:该语法是实验性语法,但是,由于babel的存在可以直接使用
state= {
count:10
}
onIncrement = () => {
this.setState({
count:this.state.count+1
})
}
// render()
<button onClick={this.onIncrement}>+1</button>
7. 表单处理
7.1 受控组件(推荐)
● HTML中的表单元素是可输入的,也就是有自己的可变状态
● 而,React中可变状态通常保存在state中,并且只能通过setState()方法来修改
● 两者冲突
● React将 state与表单元素值value绑定到一起,由state的值来控制表单元素的值
● 受控组件:其值受到React控制的表单元素
步骤:
-
在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
-
给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)
state = { txt: .. } <input type="text" value={this.state.txt} onChange={e => this.setState({ txt: e.target.value }) } />
handleChange=e=>{ this.setState({ txt: e.target.value }) } render() { return ( <input type="text" value={this.state.txt} onChange={this.handleChange} /> ) }
-
文本框 富文本框 下拉框 都是通过 value 和 onChange 处理
value={this.state.txt} onChange={this.handleChange}
handleChange=e=>{this.setState({ txt: e.target.value }) }
-
复选框 是通过 checked 和 onChange 处理
checked={this.state.isChecked} onChange={this.handleChecked}
handleChange=e=>{ this.setState({ isChecked: e.target.checked }) }
多表单元素优化:
● 问题: 每个表单元素都有一个单独的事件处理程序处理太繁琐
● 优化:使用一个事件处理程序同时处理多个表单元素
多表单元素优化步骤:
- 给表单元素添加name属性,名称与state相同
- 根据表单元素类型获取对应值
- 在change事件处理程序中通过[name]来修改对应的state
完整代码:
class Hello extends React.Component {
state = {
txt: '',
content: '',
city: 'sh',
isChecked: false,
}
//方式一
render() {
return (
<input type="text" value={this.state.txt}
onChange={e => this.setState({ txt: e.target.value }) }
/>
)
}
//方式二
handleChange=e=>{
this.setState({ txt: e.target.value })
}
render() {
return (
<input type="text" value={this.state.txt}
onChange={this.handleChange}
/>
)
}
//优化
handleForm=e=>{
const target = e.target
//根据表单元素类型获取值
const value = target.type === 'checkbox'? target.checked: target.value
const name = target.name
//根据name设置对应state
this.setState( {
[name] : value
})
}
render() {
return (
<div>
<input type="text" name="txt" value={this.state.txt} onChange= {this.handleForm} />
<textarea name="content" value={this.state.content} onChange= {this.handleForm}></textarea>
<select name="city" value={this.state.city} onChange= {this.handleForm}>
<option value='sh'>sh</option>
<option value='gz'>gz</option>
<option value='bj'>bj</option>
</select>
<input type="checkbox" name="isChecked" checked={this.state.isChecked} onChange={this.handleForm} />
</div>
)
}
}
ReactDOM.render(<Hello/>,document.getElementById('root'))
7.2 非受控组件(不推荐)
使用步骤:
-
调用React.createRef()方法创建一个ref对象
constructor () { super () this.txtRef = React.createRef () }
-
创建好的ref对象添加到文本框中
<input type="text" ref={this.txtRef} />
-
通过ref对象获取到文本框的值
console.log (this.txtRef.current.value)
完整代码:
class Hello extends React.Component {
//1.调用React.createRef()方法创建一个ref对象
constructor() {
super()
this.txtRef = React.createRef()
}
handleClick = () => {
//3.通过ref对象获取到文本框的值
console.log(this.txtRef.current.value)
}
render() {
return (
<div>
{/* 2.将创建好的ref对象添加到文本框中 */}
<input type="text" ref={this.txtRef} />
<button onClick={this.handleClick}>获取输入框的值</button>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('root'))
8. 总结–React组件基础
- 组件的两种创建方式:函数组件和类组件
- 无状态(函数)组件,负责静态结构展示
- 有状态(类)组件,负责更新UI ,让页面动起来
- 绑定事件注意this指向问题
- 推荐使用受控组件来处理表单
- 完全利用JS语言的能力创建组件,这是React的思想
案例:
-
渲染评论列表(列表渲染)
① 在state中初始化评论列表数据
② 使用数组的map方法遍历state中的列表数据
③ 给每个被遍历的Ii元素添加key属性 -
没有评论数据时渲染:暂无评论(条件渲染)
① 判断列表数据的长度是否为0
② 如果为0 ,则渲染暂无评论 -
获取评论信息,包括评论人和评论内容(受控组件)
① 使用受控组件方式处理表单元素
-
发表评论,更新评论列表(setState())
① 给按钮绑定单击事件
② 在事件处理程序中,通过state获取评论信息
③ 将评论信息添加到state中,并调用setState()方法更新state④ 边界情况:清空文本框
⑤ 边界情况:非空判断