原
一.概念
组件就是页面中的一部分,是React的一等公民,使用React就是在用组件,而所谓的组件化开发就是采用分而治之的思想来管理繁杂的页面逻辑
二.函数式组件
概念
通过函数创建出来的组件,又称简单组件或无状态组件(React16.8以后函数式组件也可以拥有自己的状态),本质上来说就是一个JS函数。
步骤
1.先定义
a,函数名称必须以大写字母开头。
b.必须有返回值,返回值表示该组件的结构,如果不想渲染任何内容,可以用 retrun null
注意点
函数式组件中的this指向是underfined,因为Babel编译后的代码开启了严格模式,Babel试一试
import ReactDOM from 'react-dom'
function Hello() {
return <div>这是第一个函数组件</div>
}
// const Hello = () => <h1>这是一个函数组件!</h1>;
// 把函数的名字作为标签名进行渲染,可以使用单闭合,或双标签的形式
ReactDOM.render(<Hello />, document.getElementById('root'))
React解析<Hello/>标签,发现是大写开头的会被当做组件进行解析,解析的时候又发现其是一个函数式组件,随后会调用此函数,将返回虚拟DOM转为真实DOM,并渲染到页面中。
三.类组件
a.使用ES6语法的class创建的组件,又称复杂组件或有状态组件。
b.类名称也必须要大写字母开头。
c.类组件应该继承React.component父类,从而可以使用父类中提供的方法或属性。
d.类组件必须提供render()方法,此方法中的this指向此组件的实例化对象,此方法中必须有return返回值。
class Hello extends React.Component {
render() {
return <div>这是第一个类组件</div>
}
}
ReactDOM.render(<Hello />, document.getElementById('root'))
了解ReactDOM.render()解析类式组件的过程:React解析<Hello/>标签,发现是大写开头的会被当做组件进行解析,解析的时候又发现其是一个类组件,会自动的new出来该类的实例,并通过实例调用原型上的render()方法,将render()方法返回的虚拟DOM转为真实DOM,并渲染到页面中。
四.提供组件
实现
1.创建APP.js创建组件(函数或类)
2.在APP.js中通过export default默认导出该组件
3.在index.js中通过import默认导入APP组件
4.渲染组件
代码
App.js
import React, { Component } from 'react'
export default class App extends Component {
render() {
return <div>Hello World</div>
}
}
index.js
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.querySelector('#root'))
五.组件的状态
概念
状态就是用来描述事物在某一时刻的数据,列如:9.23时存入的数量,18岁时人的身高等。
特点
状态能被改变,改变了之后视图会有对应的变化
作用
保存数据
数据变化响应到视图(React包内部的操作)
有状态组件/无状态组件
组件本身就不需要有状态,例如渲染一段静态的内容
组件本身就没用状态,有可能只需要从外部传入的状态就够了
六.类组件的状态
第一种方式:在constructor中通过this.state={}
class App extends React.Component {
constructor() {
super()
this.state = {
list: [
{ id: 1, name: '明天会更好' },
{ id: 2, name: '今天' },
],
}
}
}
第二方式:通过state来定义状态,state对应的值必须是一个对象。
class App extends React.Component {
state = {
list: [
{ id: 1, name: '明天会更好' },
{ id: 2, name: '今天' },
],
}
// ...
}
七.事件绑定
语法
<元素 事件名1={事件处理函数1} 事件名2={事件处理函数2}></元素>
注意:React事件名采用驼峰命名法,比如onClick,onMouseEnter等
八.类组件中事件绑定
需求:点击按钮控制台打印'Hello World'
class App extends React.Component {
render() {
return (
<button
onClick={() => {
console.log('Hello World')
}}
>
click
</button>
)
}
}
也可以把事件处理函数进行提取。
class App extends React.Component {
handleClick() {
console.log('Hello World')
}
render() {
return <button onClick={this.handleClick}>click</button>
}
}
函数式组件中的事件绑定
const App = () => {
const handleClick = () => {
console.log('Hello World')
}
return <button onClick={handleClick}>click</button>
}
九.获取事件对象
通过形参e可以拿到事件对象,例如e.target就是触发事件的那个DOM元素。
十.点击计数
实现
1.定义state
import React, { Component } from 'react'
export default class App extends Component {
// 直接使用赋值语句
state = {
count: 0,
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
</div>
)
}
}
2.绑定点击事件
class App extends Component {
state = {
count: 0,
}
handleClick() {
// Cannot read properties of undefined (reading 'state')
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
注意:this.handleClick不要加括号,加括号表示立即调用,期望的是点击按钮的时候才调用
3.分析原因
render函数是被组件实例调用的(可以修改render函数的名字来观察到),因此render函数中的this
当前组件实例,所以在render函数中通过this实例访问state和onClick没有问题,
但!<button onClick={this.handleClick}>+1</button>这样写,本质上是把this.handleClick这个方法赋值给了onClick这个属性,当点击按钮的时候,由React内部直接调用onClick,那么this指向就是
underfiend(class的内部,开启了局部严格模式,所以this不会指向window)
十一.解决this指向问题
高阶函数:通过this来直接调用handleClick并返回箭头函数
柯里化:通过函数调用继续返回函数的形式,实现多次接收参数最后统一处理的函数编码形式。
class App extends React.Component {
state = {
count: 0,
}
handleClick() {
// 这里的 this 指向是什么?那就看是谁调用的!
return () => {
console.log(this.state.count)
}
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick()}>+1</button>
</div>
)
}
}
方法2
包裹一层箭头函数
箭头函数中的this指向外部,即render函数,而render函数中的this正是组件实例。
class App extends Component {
state = {
count: 0,
}
handleClick() {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={() => this.handleClick()}>+1</button>
</div>
)
}
}
方法3
使用bind
class App extends Component {
state = {
count: 0,
}
handleClick() {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick.bind(this)}>+1</button>
</div>
)
}
}
扩展
关于class中的实例方法和原型方法?
原型方法演示
class App {
handleClick() {}
}
const app1 = new App()
const app2 = new App()
// 通过打印也能发现 handleClick 确实是挂载到原型上的
console.log(app1)
// 每一个实例访问到的都是挂载到原型上的方法,所以等价
console.log(app1.handleClick === app2.handleClick)
实例方法演示
class App {
handleClick = () => {}
}
const app1 = new App()
const app2 = new App()
// 通过打印也能发现 handleClick 确实是挂载到实例上的
console.log(app1)
// 每一个实例访问到的都是挂载到自己上的方法,所以不等价
console.log(app1.handleClick === app2.handleClick)
所以,要明白在class中直接写的方法和通过赋值语句添加的方法本质上不一样
注意:在constructor中挂载的方法也是实例方法
方法4
通过赋值语句往实例上面添加一个箭头函数。
class App extends Component {
state = {
count: 0,
}
handleClick = () => {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
十二.状态的不可变
解释
也就是说不要直接修改原数据,而要产生一份新数据,然后通过 setState()用新的数据覆盖原数据,这么做其中一个重要原因就是为了SCU,为了性能优化。
十三.受控表单组件
能够使用受控组件的方式收集到表单中的数据
概念
受控不受控一般是针对表单说的,所谓受控表单组件,即表单元素的value值受到了React中状态
(satae)的控制(对状态的操作会影响视图,视图的变化又会反映到状态中)
input
1.在state中添加一个状态,作为表单元素的value值(数据影响视图)
2.给表单元素绑定onChange事件,即表单元素的值设置为state的值(视图影响数据)。
import React from 'react'
export default class App extends React.Component {
state = {
username: '',
}
changeText = (e) => {
this.setState({
username: e.target.value,
})
}
render() {
const { username } = this.state
return (
<ul>
<li>
<label htmlFor='username'>用户名</label>
<input id='username' type='text' value={username} onChange={this.changeText} />
</li>
</ul>
)
}
}
textarea
操作方式和input框一样
import React from 'react'
export default class App extends React.Component {
state = {
content: '',
}
changeTextArea = (e) => {
this.setState({
content: e.target.value,
})
}
render() {
const { content } = this.state
return (
<ul>
{/* ... */}
<li>
<label htmlFor='content'>其他信息</label>
<textarea id='content' cols='30' rows='10' value={content} onChange={this.changeTextArea}></textarea>
</li>
</ul>
)
}
}
非受控表单组件
概念
非受控组件则是通过操作DOM的方式来获取数据,表单中的value并没有和state中的数据进行绑定
内容
通过React.createRef()获取DOM
import React, { Component } from 'react'
export default class App extends Component {
// Step1
input = React.createRef()
handleChange = () => {
// Step3
console.log(this.input.current.value)
}
render() {
return (
<div>
{/* Step2 */}
<input ref={this.input} type='text' placeholder='输入内容' onChange={this.handleChange} />
</div>
)
}
}