React组件
1.组件基本介绍
- 组件是React中最基本的内容,使用React就是在使用组件
- 组件表示页面中的部分功能
- 多个组件可以实现完整的页面功能
- 组件特点:可复用,独立,可组合
2.React创建组件的两种方式
2.1 函数组件
函数组件:使用JS的函数或者箭头函数创建的组件
- 为了区分普通标签,函数组件的名称必须
大写字母开头
- 函数组件
必须有返回值
,表示该组件的结构 - 如果返回值为null,表示不渲染任何内容
使用函数创建组件
function Hello () {
return (
<div>这是我的函数组件</div>
)
}
使用箭头函数创建组件
const Hello = () => <div>这是一个函数组件</div>
使用组件
ReactDOM.render(<Hello />, document.getElementById('root'))
例子:
01-函数组件-基本使用
import React from 'react'
import ReactDOM from 'react-dom'
/*
1. 通过函数创建一个组件即可
1. 组件的名字必须是大写开头 为了区分html原有的标签
2. 组件必须返回一段结构
3. 如果组件不想渲染任何的内容 也需要return null
*/
function Hello() {
return <div>我是hello组件</div>
}
const element = (
<div>
<h1>函数组件</h1>
{/* 使用组件 */}
<Hello></Hello>
<Hello></Hello>
</div>
)
ReactDOM.render(element, document.getElementById('root'))
02-函数组件-箭头函数
import React from 'react'
import ReactDOM from 'react-dom'
const Hello = () =><div>我是Hello组件</div>
const element = (
<div>
<h1>函数组件</h1>
{/* 使用组件 */}
<Hello></Hello>
<Hello></Hello>
</div>
)
ReactDOM.render(element, document.getElementById('root'))
2.2 类与继承
2.2.1 class 基本语法
- 在 ES6 之前通过构造函数创建对象
- 在 ES6 中新增了一个关键字 class,类 和构造函数类似,用于创建对象
-类与对象的区别
-类:指的是一类的事物,是个概念,比如车 手机 水杯等
-对象:一个具体的事物,有具体的特征和行为,比如一个手机,我的手机等, 类可以创建出来对象。 - 类创建对象的基本语法
-基本语法class 类名{}
-构造函数constructor
的用法,创建对象
-在类中提供方法,直接提供即可
-在类中不需要使用,分隔
/* class 类 extends 继承 */
// function Teacher(name, age) {
// this.name = name
// this.age = age
// }
// Teacher.prototype.sayHi = function () {
// console.log('大家好,我是' + this.name)
// }
// const stu = new Teacher('松哥', 29)
// console.log(stu)
// stu.sayHi()
// class是一个语法糖
class Teacher {
// 构造函数
constructor(name, age) {
this.name = name
this.age = age
}
sayHi() {
console.log('大家好,我是' + this.name)
}
sing() {
console.log('能够唱歌')
}
}
const stu = new Teacher('松哥', 29)
console.log(stu)
stu.sing()
2.2.2 extends 实现继承
- extends 基本使用
- 类可以使用它继承的类中所有的成员(属性和方法)
- 类中可以提供自己的属性和方法
- 注意:如果想要给类中新增属性,必须先调用 super 方法
/*
Person 人
Chinese 中国人
African 非洲人
*/
class Person {
constructor(name, gender) {
this.name = name
this.gender = gender
}
eat() {
console.log('都会吃')
}
}
class Chinese extends Person {
constructor(name, gender) {
// 父类的构造函数
super(name, gender)
this.skin = 'yellow'
}
pingpong() {
console.log('打乒乓球')
}
}
const c1 = new Chinese('姚明', 40)
console.log(c1)
c1.eat()
c1.pingpong()
class African extends Person {
constructor(name, gender) {
super(name, gender)
this.skin = 'black'
}
run() {
console.log('跑的贼快')
}
}
const xh = new African('小黑', 30)
console.log(xh)
2.3 将组件提取到单独的js文件中
项目中的组件多了之后,该如何组织这些组件呢?
- 选择一:将所有组件放在同一个JS文件中
- 选择二:将每个组件放到单独的JS文件中
- 组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中
实现方式
-
创建Hello.js
-
创建组件(函数 或 类)
-
在 Hello.js 中导出该组件
-
在 index.js 中导入 Hello 组件
-
渲染组件,
05-类组件的语法
import { Component } from 'react'
import ReactDOM from 'react-dom'
/*
1. 类组件必须继承React.Component
2. 必须提供render方法
3. render方法必须由返回值,需要返回一段结构
*/
class Hello extends Component {
render() {
return <div>我是hello组件</div>
}
}
const element = (
<div>
<h1>类组件</h1>
<Hello></Hello>
<Hello></Hello>
</div>
)
ReactDOM.render(element, document.getElementById('root'))
06-把组件抽取到独立的js中
Demo
const Demo = () => <div>我是一个函数组件</div>
export default Demo
Hello
import { Component } from 'react'
class Hello extends Component {
render() {
return <div>我是一个Hello组件</div>
}
}
export default Hello
index
import ReactDOM from 'react-dom'
import Hello from './components/Hello.jsx'
import Demo from './components/Demo.jsx'
const element = (
<div>
<h1>类组件</h1>
<Hello></Hello>
<Demo></Demo>
</div>
)
ReactDOM.render(element, document.getElementById('root'))
2.4 有状态组件和无状态组件
- 函数组件又叫做无状态组件 函数组件是不能自己提供数据【前提:基于hooks之前说的】
- 类组件又叫做有状态组件 类组件可以自己提供数据,,,,组件内部的状态(数据如果发生了改变,内容会自动的更新)数据驱动视图
- 状态(state)即组件的私有数据,当组件的状态发生了改变,页面结构也就发生了改变。
- 函数组件是没有状态的,只负责页面的展示(静态,不会发生变化)性能比较高
- 类组件有自己的状态,负责更新UI,只要类组件的数据发生了改变,UI就会发生更新。
- 在复杂的项目中,一般都是由函数组件和类组件共同配合来完成的。【增加了使用者的负担,所以后来有了hooks】
比如计数器案例,点击按钮让数值+1, 0和1就是不同时刻的状态,当状态从0变成1之后,UI也要跟着发生变化。React想要实现这种功能,就需要使用有状态组件来完成。
2.5 类组件的状态
- 状态
state
即数据,是组件内部的私有数据
,只有在组件内部可以使用 state的值是一个对象
,表示一个组件中可以有多个数据- state的基本使用
07-类组件提供状态
import { Component } from 'react'
import ReactDOM from 'react-dom'
/*
给类组件提供状态
*/
class App extends Component {
constructor() {
super()
// 给this增加一个属性 state
this.state = {
msg: 'hello',
count: 0,
}
}
render() {
return (
<div>
<h1>我是根组件</h1>
<div>{this.state.msg}</div>
<div>{this.state.count}</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
09-类组件-提供状态简写
import { Component } from 'react'
import ReactDOM from 'react-dom'
/*
给类组件提供状态
*/
class App extends Component {
// constructor() {
// super()
// // 给this增加一个属性 state
// this.state = {
// msg: 'hello',
// count: 0,
// }
// }
state = {
msg: 'hello',
count: 0,
}
render() {
return (
<div>
<h1>我是根组件</h1>
<div>{this.state.msg}</div>
<div>{this.state.count}</div>
<button>+1</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
3 事件处理
3.1 注册事件
React注册事件与DOM的事件语法非常像
语法on+事件名={事件处理程序}
比如onClick={this.handleClick}
注意:React事件采用驼峰命名法,比如onMouseEnter
, onClick
class App extends React.Component {
render() {
return (
<div>
<button onClick={this.handleClick}>点我</button>
</div>
)
}
handleClick() {
console.log('点击事件触发了')
}
}
3.2 事件对象
可以通过事件处理程序的参数获取到事件对象
function handleClick(e) {
e.preventDefault()
console.log('事件对象', e)
}
<a onClick={this.handleClick}>点我,不会跳转页面</a>
3.3 指向问题
事件处理程序中的this指向的是undefined
render方法中的this指向的而是当前react组件。只有事件处理程序中的this有问题
class App extends React.Component {
state = {
msg: 'hello react'
}
handleClick() {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={this.handleClick}>点我</button>
</div>
)
}
}
例子:
10-类组件-注册事件的基本使用
import { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
count: 0,
}
render() {
return (
<div>
<h1>我是根组件</h1>
<div>{this.state.count}</div>
<button onClick={this.clickFn} onMouseEnter={this.mouseFn}>
+1
</button>
<a href="http://www.baidu.com" onClick={this.clickFn}>
百度一下
</a>
</div>
)
}
clickFn(e) {
//e.preventDefault()会阻止表单提交的时候 重新加载页面。
e.preventDefault()
//e.stopPropagation()阻止事件冒泡。
e.stopPropagation()
console.log('点击事件')
}
mouseFn() {
console.log('鼠标进入事件')
}
}
ReactDOM.render(<App />, document.getElementById('root'))
11-类组件-事件中的this问题
import { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
count: 0,
}
render() {
console.log('render', this)
return (
<div>
<h1>我是根组件</h1>
<div>{this.state.count}</div>
<button onClick={this.clickFn}>+1</button>
</div>
)
}
clickFn() {
console.log(this)
console.log('点击事件')
console.log(this.state.count)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
3.4 this指向问题解决方案
3.4.1 在render中使用箭头函数
箭头函数的特点:自身没有this,访问的是外部的this
方式1:
class App extends React.Component {
state = {
msg: 'hello react'
}
render() {
return (
<div>
<button onClick={() => { console.log(this.state.msg) }>点我</button>
</div>
)
}
}
缺点:会把大量的js处理逻辑放到JSX中,将来不容易维护
方式2
class App extends React.Component {
state = {
msg: 'hello react'
}
handleClick() {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={() => {this.handleClick()}}>点我</button>
</div>
)
}
}
缺点:把大量的js逻辑写在了JSX结构中,不好维护
3.4.2 bind修改this指向
class App extends React.Component {
state = {
msg: 'hello react'
}
handleClick() {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={this.handleClick.bind(this)}>点我</button>
</div>
)
}
}
或者
class App extends React.Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}
state = {
msg: 'hello react'
}
handleClick() {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={this.handleClick}>点我</button>
</div>
)
}
}
3.4.3 class实例方法
class App extends React.Component {
state = {
msg: 'hello react'
}
handleClick = () => {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={this.handleClick}>点我</button>
</div>
)
}
}
注意:这个语法是试验性的语法,但是有babel的转义,所以没有任何问题
例子:
import { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
count: 0,
}
// 在类组件中render的this不会有问题,,,this就指向当前组件
// 解决react类组件中注册事件的this问题
// 1. 把函数调用包裹在箭头函数中
// 原本写法: onClick={this.clickFn}
// 写法 onClick={() => this.clickFn()}
// 2. 使用bind修改this的指向
// onClick={this.clickFn}
// bind优化 onClick={this.clickFn.bind(this)}
// 3. class新语法: 类实例语法
// onClick={this.clickFn}
// 把方法写法修改一下 clickFn = () => { console.log(this) }
render() {
return (
<div>
<h1>我是根组件</h1>
<div>{this.state.count}</div>
<button onClick={this.clickFn}>+1</button>
</div>
)
}
clickFn = () => {
console.log('123', this)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
4 setState修改状态
-
组件中的状态是可变的
-
语法
this.setState({要修改的数据})
-
注意:不要直接修改state中的值,必须通过
this.setState()
方法进行修改 -
setState
的作用- 修改state
- 更新UI
-
思想:数据驱动视图
import { Component } from 'react'
import ReactDOM from 'react-dom'
/*
给类组件提供状态
*/
class App extends Component {
state = {
count: 0,
list: ['张三', '李四', '王五'],
}
render() {
return (
<div>
<h1>我是根组件</h1>
<div>{this.state.count}</div>
<ul>
{this.state.list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={this.clickFn}>+1</button>
<button onClick={this.add}>增加数据</button>
</div>
)
}
/*
vue:
vue会通过es5的语法Object.defineProperty() vue3.0 会通过es6的proxy语法 监测到数据的改变,,,当数据改变的时候,vue会帮助我们自动更新DOM
使用vue的时候,只需要直接修改数据即可。
react:
react并不会去监听数据的变化,,,所以直接修改数据,react不知道。DOM就不会自动更新。
react提供了一个方法,,,setState
这个方法有两个作用:1.修改state的值 2. 更新DOM
结论:
1. react中不能直接修改state中的数据
2. react中必须使用setState去修改数据。
*/
clickFn = () => {
// console.log(this.state.count)
// this.state.count++
// console.log(this.state.count)
this.setState({
count: this.state.count + 1,
})
}
add = () => {
this.setState({
list: [...this.state.list, '赵六'],
})
}
}
ReactDOM.render(<App />, document.getElementById('root'))
4.1 react中核心理念:状态不可变
- 不要直接修改react中state的值,而是提供新的值
- 直接修改react中state的值,组件并不会更新
5.表单处理
我们在开发过程中,经常需要操作表单元素,比如获取表单的值或者是设置表单的值。
react中处理表单元素有两种方式:
- 受控组件
- 非受控组件(DOM操作)
5.1 受控组件基本概念
-
HTML中表单元素是可输入的,即表单用户并维护着自己的可变状态(value)。
-
但是在react中,可变状态通常是保存在state中的,并且要求状态只能通过
setState
进行修改。 -
React中将state中的数据与表单元素的value值绑定到了一起,
由state的值来控制表单元素的值
-
受控组件:value值受到了react控制的表单元素
5.2 受控组件使用步骤
- 在state中添加一个状态,作为表单元素的value值(控制表单元素的值)
- 给表单元素添加change事件,设置state的值为表单元素的值(控制值的变化)
class App extends React.Component {
state = {
msg: 'hello react'
}
handleChange = (e) => {
this.setState({
msg: e.target.value
})
}
render() {
return (
<div>
<input type="text" value={this.state.msg} onChange={this.handleChange}/>
</div>
)
}
}