目录
React组件介绍
- 组件化是React的核心思想
- 组件可以实现页面中的部分功能
- 组合多个组件可以实现完整的页面功能
- 特点:可复用、独立、可组合
React组件的两种创建方式
使用函数创建组件
函数组件
使用JS的函数(或箭头函数)创建的组件
约定1:函数名称必须以大写字母开头
约定2:函数组件必须有返回值,表示该组件的结构。如果不需要返回内容需要写返回null
渲染函数组件
用函数名作为组件标签名
组件渲染可以是单标签也可以是双标签
示例
function Hello() {
return (
<div>这是我的第一个函数组件!</div>
)
}
ReactDOM.render(<Hello/>,document.getElementById('component'))
在新的js文件中写入代码,在index.js导入新建的js文件,在index.html中添加id为component的div,将函数组件渲染到component节点上。
使用类创建组件
类组件:使用ES6的class创建的组件
约定1:类名称也必须以大写字母开头
约定2:类组件应该继承React.Component父类,从而可以使用父类中提供的方法或者属性
约定3:类组件必须提供render()方法
约定4:render()方法必须有返回值,表示该组件的结构
class Hello extends React.Component{
render() {
return (
<div>这是我的第一个类组件,哈哈哈哈</div>
)
}
}
ReactDOM.render(<Hello/>,document.getElementById('component2'))
抽离为独立的JS文件
多个组件存在时,一般都会放到一个单独的JS文件中
步骤
- 创建Demo.js
- 在Demo.js中导入React
- 创建组件(函数或类)
- 在Demo.js中导出该组件
- 在index.js中导入Hello组件
Demo.js
//创建文件
// 导入React
import React from 'react'
// 创建组件
class Demo extends React.Component{
render() {
return<div> Hello Class Component!</div>
}
}
// 导出组件
export default Demo
index.js
//导入Demo组件
import Demo from './2抽离为独立的js文件'
// 渲染导入的组件
ReactDOM.render(<Demo/>,document.getElementById('export-component'))
问题记录
在Demo.js中一开始我导入了
import React from 'react'
import ReactDOM from 'react-dom'
之后按照步骤去抽离js文件,结果出现报错和警告,报错是
Parsing error: Identifier 'React' has already been declared.
解析错误,标识符React已经被声明,就是重复导入就会出现解析报错
删除第一行之后
(警告)'ReactDOM' is defined but never used
就是没有使用就不用导入了抽离js并没有在本脚本中渲染组件所以用不着reactdom。
React事件处理
事件绑定
React事件绑定语法与DOM事件语法相似
语法:on+事件名称={事件处理程序},比如onClick={()=>{}}
注意:React事件采用驼峰命名法,比如:onMouseEnter,onFocus
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
handleClick() {
console.log('单击事件触发了')
}
render() {
return (
<button onClick={this.handleClick}>点我</button>
)
}
}
// 渲染组件
ReactDOM.render(<App/>, document.getElementById('shijianbangding'));
不要忘记在index.html创建渲染到的目标(某个div) index.js 引入本js脚本
// 通过函数组件绑定事件
function App() {
function handleClick() {
console.log('通过函数组件绑定函数,单击事件触发了')
}
return (
<button onClick={handleClick}>点我</button>
)
}
记录问题
在函数组件中绑定事件中
// 通过函数组件绑定事件
function App() {
function handleClick() {
console.log('通过函数组件绑定函数,单击事件触发了')
}
return (
<button onClick={this.handleClick}>点我</button>
)
}
Line 17:14: 'handleClick' is defined but never used
事件对象
可以通过事件处理程序的参数获取到事件对象
React中的事件对象叫做合成事件(对象)
合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
handleClick(e) {
//阻止浏览器的默认行为
e.preventDefault()
console.log('a标签的单击事件触发了')
}
render() {
return (
<a href='http://www.baidu.com' onClick={this.handleClick}>百度</a>
)
}
}
//渲染组件
ReactDOM.render(<App/>,document.getElementById('shijianduixiang'))
有状态组件和无状态组件
函数组件又叫做无状态组件,类组件又叫做有状态组件
状态(state)即数据
函数组件、无状态组件、展示型组件主要关注UI的展示;
类组件、有状态组件、容器型组件主要关注数据逻辑;
在适合的情况下,我们都应该且必须使用无状态组件。无状态组件不像其他两种方法在调用时会创建新实例,它创建时始终保持了一个实例,避免了不必要的检查和内存分配,做到了内部优化。——摘自《深入 React 技术栈》
无状态组件(木偶组件)特点
- 不能参与组件的各个生命周期的管理
- 不能被实例化
有状态组件的特点
- 用来处理数据或者页面逻辑交互
- 类组件可以维护自身的状态变量,即组件的
state
- 有状态组件组件还有不同的生命周期方法,可以让开发者能够在组件的不同阶段(挂载、更新、卸载),对组件做更多的控制。
参考文献
浅析React有状态组件和无状态组件 - 简书 (jianshu.com)
组件中的state和setState()
state的基本使用
状态(state)即数据,是组件内部的私有数据,只能在组件内部使用
state的值是对象,表示一个组件中可以有多个数据
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
//constructor() {
//super()//ES6要求
//初始化state
// this.state = {
// count:2023
// }
//}
// 简化语法
state = {
count:2023
}
render() {
return (
<div>
<h1>计数器:{ this.state.count}</h1>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<App/>,document.getElementById('state'))
显示结果
初始化state
constructor() {
super()//ES6要求
//初始化state
this.state = {
count:2023
}
根据ES6简化语法(推荐)
state = {
count:2023
}
setState()修改状态
- 状态是可变的
- 语法:this.setState({要修改的数据})
- 注意:不要直接修改state中的值
- 作用:1.修改state 2.更新UI
render() {
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={() => {
this.setState({
count:this.state.count+1
})
}}>+1</button>
</div>
)
}
JSX中掺杂过多JS逻辑代码,非常混乱,因此,把逻辑代码抽离到单独的方法中,保证JSX结构清晰。
记录问题
//事件处理程序 onIncrement() { console.log('事件处理程序中的this:', this); this.setState({ count:this.state.count+1 }) } render() { return ( <div> <h1>计数器:{this.state.count}</h1> <button onClick={this.onIncrement}>+1</button> </div> ) }
原因:事件处理程序中的this值是undefined
希望:this指向组件实例(render方法中的this即为组件实例)
改正后的代码在下一节
事件绑定this指向
1.箭头函数
利用箭头函数自身不绑定this的特点
render()方法中的this为组件实例,可以获取到setState()
import React from 'react'
import ReactDOM from 'react-dom'
class Arrow extends React.Component{
state = {
count:2023
}
//事件处理程序
onIncrement1() {
console.log('事件处理程序中的this:', this);
this.setState({
count:this.state.count+1
})
}
render() {
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={()=>this.onIncrement1()}>+1</button>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Arrow/>,document.getElementById('jiantou'))
在改bug的时候,没有任何报错,button无效,最后发现是onIncrement方法没有写(),😓
2.Function.prototype.bind()
利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起
constructor() {
super()
this.onIncrement=this.onIncrement.bind(this)
}
import React from 'react'
import ReactDOM from 'react-dom'
class Bind extends React.Component{
state = {
count:2023
}
constructor() {
super()
this.onIncrement=this.onIncrement.bind(this)
}
//事件处理程序
onIncrement() {
console.log('事件处理程序中的this:', this);
this.setState({
count:this.state.count+1
})
}
render() {
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.onIncrement}>bind+1</button>
</div>
)
}
}
// 渲染组件
ReactDOM.render(<Bind/>,document.getElementById('bind'))
3.class的实例方法
利用箭头函数形式的class实例方法
注意:该语法是实验性语法,但是,由于babel的存在可以直接使用
//事件处理程序
onIncrement=() =>{
console.log('事件处理程序中的this:', this);
this.setState({
count:this.state.count+1
})
}
总结
1.推荐使用class的实例方法
class Hello extends React.Component{
onIncrement = () => {
this.setState({……})
}
}
2.箭头函数
< button onClick = {()=> this.inCrement()}/>
3.bind
constructor(){
super()
this.onIncrement=this.onIncrement.bind(this)
}
表单处理
受控组件
html中的表单元素是可输入的,有自己的可变状态
react的可变状态通常保存在state中,并且只能通过setState()方法来修改
React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值
<input type="text" value={this.state.txt} />
步骤
1.给state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
2.给表单元素绑定change事件,将表单元素的值设置为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}></input>
</div>
)
}
}
多个受控组件的实例
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
state = {
txt: '',
content: '',
city: 'sh',
isChecked:false
}
// 处理文本框的变化
handleChange = e => {
this.setState({
txt:e.target.value
})
}
// 处理富文本框的变化
handleContent = e => {
this.setState({
content:e.target.value
})
}
// 处理下拉框的变化
handleCity = e => {
this.setState({
})
}
render() {
return (
<div>
{/* 文本框 */}
<input type='text'value={this.state.txt} onChange={this.handleChange}></input>
<br />
{/* 富文本框 */}
<textarea value={this.state.content} onChange={this.handleContent}></textarea>
<br />
{/* 下拉框 */}
<select value={this.state.city} onChange={this.handleCity}>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="gz">广州</option>
</select>
<br />
{/* 复选框 */}
<input type="checkbox"></input>
</div>
)
}
}
//渲染组件
ReactDOM.render(<App/>,document.getElementById('biaodan2'))
多表单元素优化步骤
1.给表单元素添加name属性,名称和state相同
<input type='text' name='txt' value={this.state.txt} onChange={this.handleChange}></input>
2.根据表单元素获取对应值
// 根据表单元素类型获取值
const value = target.type === 'checkbox'
? target.checked
: target.value
//根据name设置对应state
this.setState({
[name]:value
})
3.在change事件处理程序中通过[name]来修改对应的state
handleForm = e => {
//获取当前DOM 对象
const target = e.target
//根据类型获取值
const value = target.type === 'checkedbox'
? target.checked
: target.value
//获取name
const name = target.name
this.setState({
//中括号是动态获取,因为name值不同
[name]:value
})
}
render() {
return (
<div>
{/* 文本框 */}
<input type='text' name='txt' value={this.state.txt} onChange={this.handleForm}></input>
<br />
{/* 富文本框 */}
<textarea value={this.state.content} onChange={this.handleForm}></textarea>
<br />
{/* 下拉框 */}
<select value={this.state.city} onChange={this.handleForm}>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="gz">广州</option>
</select>
<br />
{/* 复选框 */}
<input name='isChecked' type="checkbox" checked={this.state.isChecked} onChange={this.handleForm}></input>
</div>
)
}
非受控组件
使用步骤
1.调用React.createRef()方法创建一个ref对象
constructor(){
super()
this.txtRef=React.createRef()
}
2.将创建好的ref对象添加到文本框中
<input type="text" ref={this.txtRef} />
3.通过ref对象获取到文本框的值
console.log(this.txtRef.current.value)
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
constructor() {
super()
// 创建ref
this.txtRef=React.createRef()
}
// 获取文本框的值
getTxt = () => {
console.log('文本框的值为:', this.txtRef.current.value);
}
render() {
return (
<div>
<input type="text" ref={this.txtRef} />
<button onClick={this.getTxt}>获取到文本框的值</button>
</div>
)
}
}
//渲染组件
ReactDOM.render(<App />, document.getElementById('biaodan3'))
React一般不直接操作DOM,所以非受控组件并不常用
总结
- 组件的两种创建方式:函数组件和类组件
- 无状态(函数)组件,负责静态结构展示
- 有状态(类)组件,负责更新UI
- 绑定事件注意this指向的问题
- 推荐使用受控组件来处理表单