一、组件概念
组件是react最重要的概念,可以说使用react就是在使用组件。组件表示页面中的部分功能,多个组件联合就能实现一个完整的页面。多个组件可以合成一个复杂组件,我们也可以把页面理解成一个由多个小组件构成的复杂大组件。
组件的特点:可复用、独立、可组合。
react声明组件的方式有两种:① 函数创建 ② 类创建(常用) ,这两种方式创建的组件主要区别在于有无状态。
二、创建组件
1、利用函数创建组件
我们可以用 JS 的函数(包括箭头函数)的来创建组件,这类组件的名称一般都以大写字母开头,函数的返回值必须存在,返回值代表着该组件要渲染的页面结构,如果返回值为null,则表示不渲染任何结构。
我们在使用创建好的函数组件时,函数名即为组件名,以标签的形式调用,可以是单标签也可以是双标签的形式。
function Hello() {
return (
<div>这是第一个函数组件</div>
)
}
// 使用组件
// 双标签
ReactDOM.render(<Hello></Hello>,root)
// 单标签(常用)
ReactDOM.render(<Hello />,root)
2、利用类创建组件(常用)
我们还可以通过 ES6 的class来创建组件,这类组件名称必须以大写字母开头 ,而且必须继承 React.Component
父类,从而使用父类中的属性和方法。组件使用render()
方法包裹 return
,return
必须有返回值,其返回值代表着组件要渲染的页面结构,如果返回值为null,则表示不渲染任何结构。
我们在使用创建好的类组件时,类名名即为组件名,以标签的形式调用,可以是单标签也可以是双标签的形式。
class Hello extends React.Component {
render() {
return <div>这是一个类组件</div>
}
}
// 使用组件
// 双标签
ReactDOM.render(<Hello></Hello>,root)
// 单标签(常用)
ReactDOM.render(<Hello />,root)
三、使用组件
在React中,组件是一个个独立的个体,所以一般每个组件都会放到一个单独的 js 文件中,这样既不容易混乱,还有利于组件复用。
一个写在独立js文件中的组件,从创建到渲染,大体分为六步:
① 创建 Hello.js 文件
② 在Hello.js文件中 导入 React
③ 在文件中创建组件(类或函数)
④ 在文件中将创建的组件进行导出
⑤ 在需要引用组件的index.js文件中导入该组件
⑥ 渲染组件
Hello.js文件:
// 导入 react
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 />, root)
四、组件的事件处理
1、事件绑定
在React组件中绑定事件的语法与原生DOM的事件绑定语法相似,但React中绑定的事件需要用 {}
包裹,也就是 on + 事件名称 = {事件处理程序}
的语法格式,例如:onClick = {() => {}}。而且React中的事件名采用了小驼峰命名法,例如:onMouseEnter
、onFocus
等。
React 组件中的事件处理程序的写法在函数组件和类组件中是有一定区别的:
函数组件中:
function APP() {
// 事件处理程序 与return同级 前面需要加function
function handleClick() {
console.log('单击事件')
}
// 返回的页面结构
return (
// 绑定事件
<button onClick={handleClick}>点击</button>
)
}
类组件中:
class APP extends React.Component {
// 事件处理程序 与render同级 前面不需要加 function
handleClick() {
console.log('单击事件')
}
// 返回的页面结构
render() {
return (
// 绑定事件
<button onClick={this.handleClick}>点击</button>
)
}
}
2、事件对象
在React 的事件处理程序中,可以通过参数,来获得事件对象,在React中又被称为合成事件对象,它能够兼容所有浏览器,无需担心跨浏览器兼容的问题。我们获得合成事件对象之后,可以通过它对事件进行操作,比如 e.preventDefult()
阻止默认行为等。
function APP() {
// 事件处理程序 参数 e 代表合成事件对象
function handleClick(e) {
// 阻止 a 跳转的默认行为
e.preventDefult()
console.log('事件对象',e)
}
// 返回的页面结构
return (
// 绑定事件
<a onClick={handleClick}>点击我,页面不会跳转</a>
)
}
五、组件的状态
1、状态的概念
组件的状态(state) 就是指组件中的私有数据,只能在组件内部进行使用。函数组件又称为无状态组件,因为其没有自己的状态,只需要静态的展示数据。而类组件被称为有状态组件,因为它有自己的状态,可以根据数据变化来动态的更新UI,动态的展示数据。
在实际项目应用中,页面中的数据通常都是会动态变化的,所以我们通常都是使用类组件。
2、状态 state 的基本使用
状态就是指组件内部的私有数据,在组件中是通过 state 来存储的,state 的值是一个对象,可以通过键值对的形式存储多个数据。我们只需要在return
的html结构中 ,通过 this.state.数据名
的形式就能获得相应的数据值。
class Hello extends React.Component {
// state 需要写在 constructor 内
constructor() {
super()
this.state = {
count: 0
}
}
render() {
return (
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
)
}
}
简化写法(常用):
class Hello extends React.Component {
// 简化写法 直接写state即可
state = {
count: 0
}
render() {
return (
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
)
}
}
3、setState() 修改状态
state 中能够存储数据,我们也可以通过 state 来调用数据,但是我们不能直接去修改state中的数据,我们只能通过 setState() 方法 来修改数据。setState() 方法在修改state中数据的同时,还会去更新页面中引用被修改数据部分的UI,使页面展示的内容也跟随数据改变而改变,这也就是数据驱动视图的思想。
语法格式: this.setState({ 数据名: 数据值 })
class Hello extends React.Component {
state = {
count: 0
}
render() {
return (
<div>
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
// 修改 state中的数据 采用箭头函数的原因下面会解释
<button onClick={() => {this.setState({ count: this.state.count+1})}}>点击+1</button>
</div>
)
}
}
六、事件绑定中的this指向问题
在上面的那个例子中,我们仅仅在JSX中修改了state中的一个数据,就显得代码比较混乱了,如果在实际项目应用中,会有更多的操作逻辑,代码会更混乱。所以我们通常都会将修改逻辑抽离出来放到一个事件中去,使JSX结构的清晰干净。像是下面这样:
class Hello extends React.Component {
state = {
count: 0
}
setCount() {
this.setState({ count: this.state.count+1})
}
render() {
return (
<div>
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
// 修改 state中的数据
<button onClick={this.setCount}>点击+1</button>
</div>
)
}
}
如果你真的这么写了,恭喜你!你上当了!
这样写是会报错的。因为在JavaScript中类的方法默认是不会绑定 this 的,所以事件处理程序的 this 指向 undefined,无法通过 this 访问到对应的数据对象。因此我们需要修改事件处理程序的this指向,让this指向组件实例。
目前常用的方法有以下三种:
1、箭头函数
箭头函数本身是没有 this 的,它的this指向取决于上一级this的指向,而 render 的this指向的是组件实例,所以箭头函数可以访问到对应的事件处理程序:
class Hello extends React.Component {
state = {
count: 0
}
setCount() {
this.setState({ count: this.state.count+1})
}
render() {
return (
<div>
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
// 修改 state中的数据 采用箭头函数调用事件处理函数
<button onClick={() => this.setCount()}>点击+1</button>
</div>
)
}
}
2、Function.prototype.bind()
利用ES5中的bind方法,将事件处理程序中的this指向修改为组件实例:
class Hello extends React.Component {
constructor() {
super();
this.state = {
count: 0
}
// 修改 this指向
this.setCount = this.setCount.bind(this)
}
setCount() {
this.setState({ count: this.state.count+1})
}
render() {
return (
<div>
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
// 修改 state中的数据
<button onClick={this.setCount}>点击+1</button>
</div>
)
}
}
3、class的实例方法(推荐)
我们还可以通过将事件处理程序的方法以箭头函数的方式进行声明,该语法虽然是实验性语法,但是由于babel的存在,也可以直接使用。
class Hello extends React.Component {
state = {
count: 0
}
setCount = () => {
this.setState({ count: this.state.count+1})
}
render() {
return (
<div>
// 使用state中的数据
<div>有状态组件的数据---{this.state.count}</div>
// 修改 state中的数据 采用箭头函数的原因下面会解释
<button onClick={this.setCount}>点击+1</button>
</div>
)
}
}