目录
一、React 组件介绍
二、创建React组件的两种方式
1. 使用函数创建组件
2. 使用类创建组件
3. 抽离为独立 JS文件
三、React 事件处理
1. 事件绑定
2. 事件对象
四、有状态组件与无状态组件
1. state的基本使用
2. setState() 修改状态
3. 从 JSX 中抽离事件处理程序
4. 事件绑定 this 指向
4.1. 箭头函数
4.2. Function.prototype.bind()
4.3. class 的实例方法
五、表单处理
1. 受控组件
1.1 概念
1.2 实现步骤
1.3 多表单元素优化
2. 非受控组件
一、React 组件介绍
- 组件是 React 的一等公民,使用React其实就是在使用组件。
- 组件表示页面中的部分功能。
- 组合多个组件实现完整的页面功能。
- 特点:可复用、独立、可组合
二、创建React组件的两种方式
1. 使用函数创建组件
- 函数组件:使用 JS 的函数(或箭头函数)创建的组件。
function Hello(){ return { <div> 函数组件 </div> } }
注意:
- 函数名称必须以大写字母开头。
- 函数组件必须有返回值,表示该组件的结构。
- 如果返回值为null,表示不渲染任何内容。
- 渲染函数组件:用函数名作为组件标签名,组件标签可以是单标签也可以是双标签。
const Hello = () => <div> 函数组件 </div> // 渲染 ReactDOM.render(<Hello />,document.getElementById('root'))
2. 使用类创建组件
- 类组件:使用 ES6 的 class创建的组件
class Hello extends React.Component { render(){ return <div> 类组件 </div> } } ReactDOM.render(<Hello />,document.getElementById('root'))
注意:
- 类名称也必须以大写字母开头。
- 类组件应该继承 React.Component 父类,从而可以使用父类中提供的方法或属性。
- 类组件必须提供 render() 方法。
- render() 方法必须有返回值,表示该组件的结构。
3. 抽离为独立 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>
}
}
// 导出 Hello 组件
export default Hello
// index.js
import Hello from './Hello'
// 渲染导入的 Hello组件
ReactDOM.render(<Hello />,root)
三、React 事件处理
1. 事件绑定
- React 事件绑定语法与DOM事件语法相似
- 语法:on + 事件名称 = { 事件处理程序 },例如:onClick = { () => {} }
- 注意:React 事件采用驼峰命名法,比如:onMouseEnter、onFocus
.// 类组件
class App extends React.Component {
handleClick() {
console.log('单击事件')
}
render() {
return {
<button onclick={this.handleClick} ><button>
}
}
}
// 函数组件
fucntion App() {
function handleClick() {
console.log('点击事件')
}
return {
<button onclick={handleClick} ><button>
}
}
2. 事件对象
- 可以通过事件处理程序的参数获取到事件对象。
- React中的事件对象叫做:合成事件(对象)。
- 合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题。
function handleClick(e) {
// 阻止浏览器的默认行为
e.preventDefault ()
console.log('事件对象',e)
}
<a href="www.baidu.com" onClick={handleClick}>点击</a>
四、有状态组件与无状态组件
- 函数组件又叫做无状态组件,类组件又叫做有状态组件。
- 状态(state)即数据,函数组件没有自己的状态,只负责数据展示(静),类组件有自己的状态,负责更新UI,让页面“动”起来。
比如计数器案例中,点击按钮让数值加1。0 和 1 就是不同时刻的状态,而由0 变 1 就表示状态发生了变化。状态变化后,UI也要相应的更新。React 中想要实现该功能,就要使用有状态组件来实现。
1. state的基本使用
- 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用。
- state的值是对象,表示一个组件中可以可以有多个数据。
class App extends React.Component { constructor() { super() // 初始化 state this.state = { count: 0 } } // ES6 简化语法 初始化state state = { count : 0 } render() { return { <div> 计数器 </div> } } } ReactDOM.render(<App />,document.getElementById('root'))
初始化完成后,接下来就需要考虑在组件结构中如何使用state:
- 获取状态:this.state
render() { return { <div> 计数器 { this.state }</div> } }
2. setState() 修改状态
- 状态是可变的。
- 语法: this.setState({ 需要修改的数据 })
- 注意:不要直接修改state的值!
class App extends React.Component { state = { count : 0 } render() { return { <div> 计数器 { this.state.count }</div> <button onClick={() => { // 正确 this.setState({ count: this.state.count + 1 }) // 错误 this.state.count +1 }}>+1</button> } } } ReactDOM.render(<App />,document.getElementById('root'))
- setState() 作用:1. 修改state ; 2. 更新UI
- 思想:数据驱动视图
3. 从 JSX 中抽离事件处理程序
- JSX 中掺杂过多的JS逻辑程序代码,会使代码显得非常混乱。
- 推荐:将逻辑抽离到单独的方法中,保证JSX结构清晰。
class App extends React.Component {
state = {
count : 0
}
// 事件处理程序
OnIncreaseNumb() {
this.setState({
count: this.state.count + 1
})
}
render() {
return {
<div> 计数器 { this.state.count }</div>
<button onClick={this.OnIncreaseNumb}>+1</button>
}
}
}
ReactDOM.render(<App />,document.getElementById('root'))
此时,会发现页面报错:
class App extends React.Component { state = { count : 0 } OnIncreaseNumb() { this.setState({ count: this.state.count + 1 }) } render() { return { <div> 计数器 { this.state.count }</div> <button onClick={this.OnIncreaseNumb}>+1</button> } } } ReactDOM.render(<App />,document.getElementById('root'))
原因:事件处理程序中 this 的值为 undefined 。
解决方案:this指向组件实例(render方法中的this即是组件实例)
4. 事件绑定 this 指向
根据上述提出的问题以及解决方案,事件绑定this指向有以下三种方式:
4.1. 箭头函数
- 利用箭头函数自身不绑定 this 的特点。
- render() 方法中的this 为组件实例,可以获取到 setState() 。
class App extends React.Component {
state = {
count : 0
}
OnIncreaseNumb() {
this.setState({
count: this.state.count + 1
})
}
render() {
return {
<div> 计数器 { this.state.count }</div>
// 箭头函数中 this 指向外部环境 此处为:render方法
<button onClick={ () => this.OnIncreaseNumb()}>+1</button>
}
}
}
ReactDOM.render(<App />,document.getElementById('root'))
4.2. Function.prototype.bind()
- 利用ES5 中 bind() 方法,将事件处理程序中的 this与组件实例绑定在一起。
class App extends React.Component {
constructor() {
state = {
count : 0
}
super()
this.OnIncreaseNumb = this.OnIncreaseNumb.bind(this)
}
OnIncreaseNumb() {
this.setState({
count: this.state.count + 1
})
}
render() {
return {
<div> 计数器 { this.state.count }</div>
<button onClick={ this.OnIncreaseNumb }>+1</button>
}
}
}
ReactDOM.render(<App />,document.getElementById('root'))
4.3. class 的实例方法
- 利用箭头函数形式的class 实例方法。
- 注意:该语法是实验性语法,但是由于 babel 的存在可以直接使用。
class App extends React.Component {
state = {
count : 0
}
OnIncreaseNumb = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return {
<div> 计数器 { this.state.count }</div>
<button onClick={ this.OnIncreaseNumb }>+1</button>
}
}
}
ReactDOM.render(<App />,document.getElementById('root'))
五、表单处理
1. 受控组件
1.1 概念
- HTML 中的表单元素是可以输入的,也就是有自己的可变状态。
- React 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改。
- React 将 state 与表单元素值 value 绑定在一起,由 state 的值来控制表单元素的值。
<input type="text" value={this.state.txt} />
所以,受控组件其实就是:其值受到React 控制的表单元素。
1.2 实现步骤
- 在state 中添加一个状态,作为表单元素的value值(控制表单元素的来源)
- 给表单元素绑定 onChange事件,将表单元素的值 设置为 state的值(控制表单元素值的变化)
class App extends React.Component {
state = {
txt: ''
}
handleChange = e => {
this.setState({
txt: e.target.value
})
}
render() {
return {
<div>
<input type="text" value={this.state.txt} onChange={this.handleChange} />
</div>
}
}
}
ReactDOM.render(<App />,document.getElementById('root'))
1.3 多表单元素优化
- 问题:每个表单元素都有一个单独的事件处理程序就使得代码变得过于繁琐。
- 优化:使用一个时间处理程序同时处理多个表单元素。
优化步骤:
- 给表单元素添加 name属性,名称与state 相同。
- 根据表单元素类型获取对应值。
- 在 onChange事件处理程序中通过 { name } 来修改对应的 state。
class App extends React.Component {
state = {
txt: '',
content: '',
city: 'sd',
isChecked: false
}
handleChange = e => {
// 获取当前DOM对象
const target = e.target
// 根据类型获取对应值
const value = target.type === 'ckeckbox'
? target.checked
: target.value
// 根据name 设置对应state
const name = target.name
this.setState({
[name]: value
})
}
render() {
return {
<div>
<input type="text" name="txt" value={this.state.txt} onChange={this.handleChange} />
<textarea name="content" value={this.state.content} onChange={this.handleContent}></textarea>
<select name="city" value={this.state.city} onChange={this.handleCity}>
<option value="sd">山东</option>
<option value="hb">河北</option>
<option value="tj">天津</option>
</select>
<br/>
<input name="checkex" type="checkbox" checked={this.state.isChecked} onChange={this.handleChecked}>
</div>
}
}
}
ReactDOM.render(<App />,document.getElementById('root'))
2. 非受控组件
- 说明:借助于ref,使用原生 DOM方式获取表单元素值。
- ref 的作用:获取 DOM 或组件。
使用步骤:
- 调用 React.createRef() 方法创建一个 ref 对象。
constructor() { super() this.txtRef = React.createRef() }
- 将创建好的 ref 对象添加到文本框中。
<input type="txt" ref={this.txtRef} />
- 通过 ref 对象获取到文本框的值。
console.log(this.txtRef.current.value)