一、組件基本介紹
- 組件是 React 開發(fā)(現(xiàn)代前端開發(fā))中最重要的內(nèi)容
- 組件允許你將 UI 拆分為獨立、可復用的部分,每個部分都可以獨立的思考
- 組合多個組件(組裝樂高積木)實現(xiàn)完整的頁面功能
- 特點:獨立、可復用、可組合
- 組件包含三部分:HTML/CSS/JS
- 展示頁面中的可復用部分
二、組件創(chuàng)建
2.1 函數(shù)組件
- 函數(shù)組件:使用JS的函數(shù)或者箭頭函數(shù)創(chuàng)建的組件
- 使用 JS 的函數(shù)(或箭頭函數(shù))創(chuàng)建的組件,叫做
函數(shù)組件
- 約定1:函數(shù)名稱必須以大寫字母開頭,React 據(jù)此區(qū)分組件和普通的 HTML標簽
- 約定2:函數(shù)組件必須有返回值,表示該組件的 UI 結(jié)構(gòu);如果不需要渲染任何內(nèi)容,則返回
null
- 使用 JS 的函數(shù)(或箭頭函數(shù))創(chuàng)建的組件,叫做
- 使用函數(shù)創(chuàng)建組件
// 使用普通函數(shù)創(chuàng)建組件:
function Hello() {
return <div>這是我的第一個函數(shù)組件!</div>
}
function Button() {
return <button>按鈕</button>
}
// 使用箭頭函數(shù)創(chuàng)建組件:
const Hello = () => <div>這是我的第一個函數(shù)組件!</div>
- 使用組件
- 組件就像 HTML 標簽一樣可以被渲染到頁面中。組件表示的是一段結(jié)構(gòu)內(nèi)容,對于函數(shù)組件來說,渲染的內(nèi)容是函數(shù)返回值對應的內(nèi)容
- 使用函數(shù)名稱作為組件標簽名稱
// 使用 雙標簽 渲染組件:
<Hello></Hello>
ReactDOM.render(<Hello></Hello>, root)
// 使用 單標簽 渲染組件:
<Hello />
ReactDOM.createRoot(document.querySelector('#root')).render(<Hello></Hello>)
小結(jié):
- 在react中可以使用函數(shù)或者箭頭函數(shù)創(chuàng)建組件
- 組件的首字母必須大寫
2.2 類組件
內(nèi)容
- 使用 ES6 的 class 創(chuàng)建的組件,叫做類(class)組件
- 約定1:類名稱也必須以大寫字母開頭
- 約定2:類組件應該繼承 React.Component 父類,從而使用父類中提供的方法或?qū)傩?/li>
- 約定3:類組件必須提供 render 方法
- 約定4:render 方法必須有返回值,表示該組件的 UI 結(jié)構(gòu)
定義組件
// 導入 React
import React from 'react'
class Hello extends React.Component {
render() {
return <div>Hello Class Component!</div>
}
}
ReactDOM.render(<Hello />, root)
// 只導入 Component
import { Component } from 'react'
class Hello extends Component {
render() {
return <div>Hello Class Component!</div>
}
}
使用組件
ReactDOM.createRoot(document.querySelector('#root')).render(<Hello></Hello>)
總結(jié)
- 使用class語法創(chuàng)建組件比函數(shù)創(chuàng)建稍微麻煩一些,但是類組件的功能會比函數(shù)組件的功能更加強大(hooks 之前)
三、將組件提取到單獨的js文件中
實現(xiàn)方式
- 創(chuàng)建 Hello.js
- 創(chuàng)建組件(函數(shù) 或 類)
- 在 Hello.js 中導出該組件
- 在 index.js 中導入 Hello 組件
- 渲染組件
核心代碼文章來源地址http://www.zghlxwxcb.cn/news/detail-604878.html
// index.js
//直接導入
import Hello from './Hello'
//按需導入
// import {Hello} from './Hello'
// 渲染導入的Hello組件
ReactDOM.createRoot(document.querySelector('#root')).render(<Hello></Hello>)
// Hello.js
import { Component } from 'react'
class Hello extends Component {
render() {
return <div>Hello Class Component!</div>
}
}
// 導出Hello組件(默認導出)
export default Hello
//按需導出
// export {
// Hello
// }
四、有狀態(tài)組件和無狀態(tài)組件
內(nèi)容:
- 函數(shù)組件又叫做無狀態(tài)組件 函數(shù)組件是不能自己提供數(shù)據(jù)【前提:不考慮 hooks 的情況下】
- 類組件又叫做有狀態(tài)組件 類組件可以自己提供數(shù)據(jù)
- 狀態(tài)state是組件的私有數(shù)據(jù),當組件的狀態(tài)發(fā)生了改變,頁面結(jié)構(gòu)也就發(fā)生了改變(數(shù)據(jù)驅(qū)動視圖)
-
函數(shù)組件是沒有狀態(tài)的,只負責頁面的展示(
靜
態(tài),不會發(fā)生變化),性能比較高 -
類組件有自己的狀態(tài),負責更新UI,只要類組件的數(shù)據(jù)發(fā)生了改變,UI 就會發(fā)生更新(
動
態(tài)) - 在項目中,一般都是由函數(shù)組件和類組件共同配合來完成的
比如計數(shù)器案例,點擊按鈕讓數(shù)值+1, 0和1就是不同時刻的狀態(tài),當狀態(tài)從0變成1之后,UI也要跟著發(fā)生變化。React想要實現(xiàn)這種功能,就需要使用有狀態(tài)組件來完成。
- 函數(shù)組件和類組件的區(qū)別:有沒有狀態(tài)【前提:不考慮 hooks 的情況下】
五、類組件的狀態(tài)
內(nèi)容:
- 狀態(tài)
state
,即數(shù)據(jù),是組件內(nèi)部的私有數(shù)據(jù),只能在組件內(nèi)部使用 - 狀態(tài) state 的值是對象,表示一個組件中可以有多個數(shù)據(jù)
- 通過
this.state.xxx
來獲取狀態(tài)
核心代碼
- 創(chuàng)建 state
class Hello extends Component {
// 為組件提供狀態(tài)
state = {
count: 0,
list: [],
isLoading: true
}
render() {
return (
<div>計數(shù)器</div>
)
}
}
- 讀取狀態(tài):通過
this.state
來獲取狀態(tài)
class Hello extends Component {
// ...
render() {
// 通過 this.state 來訪問類組件的狀態(tài)
return (
<div>計數(shù)器:{this.state.count}</div>
)
}
}
六、事件處理
6.1 注冊事件
內(nèi)容:
-
React注冊事件與DOM的事件語法非常像
-
語法
on+事件名 ={事件處理程序}
比如onClick={this.handleClick}
-
注意:React事件采用駝峰命名法,比如
onMouseEnter
,onClick
核心代碼
class App extends React.Component {
handleClick() {
console.log('點擊事件觸發(fā)')
}
render() {
return (
<div>
<button onClick={this.handleClick}>點我</button>
</div>
)
}
}
6.2 事件對象
核心代碼
- 可以通過事件處理程序的參數(shù)獲取到事件對象
- 注意:React 中的事件對象是 React 內(nèi)部處理后的事件對象,一般稱為:SyntheticBaseEvent 合成事件對象。用法與 DOM 原生的事件對象用法基本一致
function handleClick(e) {
e.preventDefault()
console.log('事件對象', e)
}
<a onClick={this.handleClick}>點我,不會跳轉(zhuǎn)頁面</a>
6.3 this指向問題
內(nèi)容:
-
事件處理程序中的this指向的是
undefined
-
render方法中的this指向的是當前react組件。只有事件處理程序中的this有問題
-
原因
- 事件處理程序的函數(shù)式函數(shù)調(diào)用模式,在嚴格模式下,this指向
undefined
- render函數(shù)是被組件實例調(diào)用的,因此render函數(shù)中的this指向當前組件
- 事件處理程序的函數(shù)式函數(shù)調(diào)用模式,在嚴格模式下,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>
)
}
}
總結(jié):
- 在react的事件處理函數(shù)中,this指向
undefined
6.4 this指向解決方案
內(nèi)容:
-
解決事件處理程序中this指向問題主要有三種方式
-
方式1:箭頭函數(shù)
class App extends React.Component {
state = {
msg: 'hello react'
}
handleClick() {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={() => this.handleClick()}>點我</button>
</div>
)
}
}
- 方式2:
bind
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>
)
}
}
- 方式3:箭頭函數(shù)形式的實例方法 - 推薦使用
class App extends React.Component {
state = {
msg: 'hello react'
}
handleClick = () => {
console.log(this.state.msg)
}
render() {
return (
<div>
<button onClick={this.handleClick}>點我</button>
</div>
)
}
}
總結(jié)
- 推薦使用方式3,箭頭函數(shù)形式的實例方法
- 解釋為什么方式3可以解決 this 指向問題:
// ES6
class Person1 {
// 類的 構(gòu)造函數(shù)
constructor() {
// 構(gòu)造函數(shù)中的 this 就是實例對象
console.log(this)
this.name = 'jack'
this.fn = () => {
console.log(this)
}
}
// 上述寫法簡化語法:
// name = 'jack'
// fn = () => {}
// 這個形式的方法會被添加到原型中
sayHi() {}
}
說明:
fn = () => {} 這種形式的語法是簡化語法(語法糖),書寫會更簡單。實際上,完整的寫法是:
constructor() {
this.fn = () => {}
}
即:在構(gòu)造函數(shù)中,給 this 添加了一個方法 fn
為什么可以解決 this 指向問題呢?
因為 constructor 中創(chuàng)建的箭頭函數(shù),內(nèi)部的 this 會指向 constructor 中的 this
而 constructor 中的 this 就是實例對象(也就是我們需要的 this)
七、setState 修改狀態(tài)
內(nèi)容:
- 語法:
this.setState({ 要修改的部分數(shù)據(jù) })
- setState() 作用:1 修改 state 2 更新 UI
- 思想:數(shù)據(jù)驅(qū)動視圖,也就是只需要修改數(shù)據(jù)(狀態(tài))那么頁面(視圖)就會自動刷新
- 核心:數(shù)據(jù)?。。?/li>
- 從現(xiàn)在開始,我們關(guān)心的是如何修改數(shù)據(jù),而不再是關(guān)心如何修改DOM
- 并且,注意:盡量避免直接手動 DOM(通過 document.querySelector() 獲取到到DOM對象然后再操作) 操作?。?!
- 注意:不要直接修改 state 中的值,這是無效的!
核心代碼
class Hello extends Component {
state = {
count: 0
}
handleClick = () => {
this.setState({
count: 10
})
}
render() {
return (
<div>
<h1>計數(shù)器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
// 在 count 當前值的基礎上加 1
this.setState({
count: this.state.count + 10
})
總結(jié):
- 能直接修改 state 的值嗎?不能
- 如何修改 React 組件中的狀態(tài)?
setState()
- setState 是哪來的?從 Component 父類繼承過來的
八、react核心理念(狀態(tài)不可變)
內(nèi)容:
- 狀態(tài)不可變指的是:不要直接修改狀態(tài)的值,而是基于當前狀態(tài)創(chuàng)建新的狀態(tài)值
核心代碼:
state = {
count: 0,
list: [1, 2, 3],
person: {
name: 'jack',
age: 18
}
}
// 【不推薦】直接修改當前值的操作:
this.state.count++
++this.state.count
this.state.count += 1
this.state.count = 1
// 只要是數(shù)組中直接修改當前數(shù)組的方法都不能用!
this.state.list.push(123)
this.state.person.name = 'rose'
// 【推薦】不是直接修改當前值,而是創(chuàng)建新值的操作:
this.setState({
count: this.state.count + 1,
list: [...this.state.list, 123],
person: {
...this.state.person,
// 要修改的屬性,會覆蓋原來的屬性,這樣,就可以達到修改對象中屬性的目的了
name: 'rose'
}
})
九、表單處理
9.1 受控組件
內(nèi)容:
- HTML中表單元素是可輸入的,即表單元素維護著自己的可變狀態(tài)(value)
- 但是在react中,可變狀態(tài)通常是保存在
state
中的,并且要求狀態(tài)只能通過setState
進行修改 - React中將state中的數(shù)據(jù)與表單元素的value值綁定到了一起,由state的值來控制表單元素的值
- 受控組件:value值受到了react狀態(tài)控制的表單元素
核心代碼:
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>
)
}
}
總結(jié):
- 使用受控組件的方式處理表單元素后,狀態(tài)的值就是表單元素的值。即:想要操作表單元素的值,只需要操作對應的狀態(tài)即可*
9.2 非受控組件-ref
非受控組件借助于ref,使用原生DOM的方式來獲取表單元素的值
內(nèi)容文章來源:http://www.zghlxwxcb.cn/news/detail-604878.html
- 受控組件是通過 React 組件的狀態(tài)來控制表單元素的值
- 非受控組件是通過手動操作 DOM 的方式來控制
- 此時,需要用到一個新的概念:
ref
- ref:用來在 React 中獲取 DOM 元素
核心代碼
// 1 導入 createRef 函數(shù)( 用來創(chuàng)建 ref 對象 )
import { createRef } from 'react'
class Hello extends Component {
// 2 調(diào)用 createRef 函數(shù)來創(chuàng)建一個 ref 對象
// ref 對象的名稱(txtRef)可以是任意值
// 命名要規(guī)范: txt(DOM 元素的自己標識) + Ref
txtRef = createRef()
handleClick = () => {
// 文本框?qū)?DOM 元素
// console.log(this.txtRef.current)
// 4 獲取文本框的值:
console.log(this.txtRef.current.value)
}
render() {
return (
<div>
{/*
3 將創(chuàng)建好的 ref 對象,設置為 input 標簽的 ref 屬性值
作用:將 ref 和 input DOM 綁定到一起,將來在通過 this.txtRef 拿到的就是當前 DOM 對象
*/}
<input ref={this.txtRef} />
<button onClick={this.handleClick}>獲取文本框的值</button>
</div>
)
}
}
到了這里,關(guān)于React之組件的分類、使用,事件對象,this指向問題,修改狀態(tài)以及受控組件與非受控組件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!