React組件三大屬性之state,props,refs
1.1基本理解和使用
1.1.1 使用React開發(fā)者工具調(diào)試
React Developer Tools
1.1.2 定義組件的方式
Ⅰ.函數(shù)式組件:
<script type="text/babel"> // 1.創(chuàng)建函數(shù)式組件 (首字母必須大寫) function MyComponent() { //(函數(shù)必須有返回值) console.log(this) //undefined (本來this指向的是window,但是由于babel翻譯完之后啟用es5的嚴格模式,自定義的函數(shù)里的this不讓指向window) return <h2>我是用函數(shù)定義的組件(適用于【簡單組件】的定義)</h2> } // 2.渲染組件到頁面 (必須寫組件標簽) ReactDOM.render(<MyComponent />, document.getElementById('test')) </script>
執(zhí)行了ReactDOM.render(…之后發(fā)生了什么?
1.React解析組件標簽,找到了MyComponent組件。
2.發(fā)現(xiàn)組件是使用函數(shù)定義的,隨后調(diào)用該函數(shù),將返回的虛擬DOM轉(zhuǎn)為真實DOM,隨后呈現(xiàn)在頁面中。
類的基本知識復(fù)習(xí)移步到vscode
Ⅱ.類式組件:
復(fù)雜組件:如果組件是有狀態(tài)的,那么就是復(fù)雜組件。
<script type="text/babel"> // 1.創(chuàng)建類式組件 class MyComponent extends React.Component { render() { // render是放在哪里的?——MyComponent的原型對象上,供實例使用 // render中的this是誰?——MyComponent的實例對象 <=> MyComponent組件實例對象 console.log('render中的this是誰:', this) return <h2>我是用類定義的組件(適用于【復(fù)雜組件】的定義)</h2> } } // 渲染組件到頁面 ReactDOM.render(<MyComponent />, document.getElementById('test'))
執(zhí)行了ReactDOM.render(…之后發(fā)生了什么?
1.React解析組件標簽,找到了MyComponent組件。
2.發(fā)現(xiàn)組件是使用類定義的,隨后new出來該類的實例,并通過該實例調(diào)用到原型上的render方法。
3.將render返回的虛擬DOM轉(zhuǎn)為真實DOM,隨后呈現(xiàn)在頁面中。
1.1.3 注意
- 組件名必須首字母大寫
- 虛擬DOM元素
只能有一個根標簽
- 虛擬DOM元素必須有
結(jié)束標簽
1.1.4 渲染類組件標簽的基本流程
- React內(nèi)部會創(chuàng)建組件實例對象
- 調(diào)用render()返回得到虛擬DOM, 并解析為真實DOM
- 插入到指定的頁面元素內(nèi)部
1.2 組件實例的三大核心屬性之一:state
1.2.1 理解
- state是組件對象最重要的屬性, 值是
對象
(可以包含多個key-value的組合) - 組件被稱為"狀態(tài)機", 通過
更新組件的state
,來更新對應(yīng)的頁面顯示
(重新渲染組件) - 數(shù)據(jù)存放在狀態(tài)里,來驅(qū)動對應(yīng)頁面的顯示
1.2.2 案例
需求: 定義一個展示天氣信息的組件
- 默認展示天氣炎熱 或 涼爽點
- 擊文字切換天氣
效果如下:
1.2.3 在類式組件使用state
<script type="text/babel"> // 1.創(chuàng)建組件 class Weather extends React.Component { // 借助構(gòu)造器初始化狀態(tài) // 構(gòu)造器調(diào)用了幾次?————1次 constructor(props) { console.log('constructor') super(props) // 初始化狀態(tài) this.state = { isHot: true, wind: '微風(fēng)' } //解決changeWeather中this指向的問題 this.changeWeather = this.changeWeather.bind(this) // 這是一個賦值語句,右邊的this.changeWeather是指順著原型鏈找到了changeWeather(),然后調(diào)用bind(),bind可以生成一個新的函數(shù),改變this指向。括號中為傳入的this,指得就是類中的實例對象 // 拿到了原型上的changeWeather,通過bind生成了一個新的changeWeather掛在實例自身 } // render調(diào)用了幾次?————1+n次 1是初始化的那次,n是狀態(tài)更新的次數(shù) render() { console.log('render') // 讀取狀態(tài) const { isHot, wind } = this.state // react:將changeWeather調(diào)用的返回值賦值給onClick,不用加括號 // 原生js:onclick時調(diào)用changeWeather函數(shù),要加括號 return <h1 onClick={this.changeWeather}>今天天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1> // 含義是:這是一個賦值語句,把右邊這個函數(shù)交給onClick作為回調(diào), // 等你點擊的時候,react幫你調(diào)changeWeather()函數(shù) } // changeWeather調(diào)用了幾次?————點幾次調(diào)幾次 changeWeather() { console.log('changeWeather') // changeWeather放在哪里?——Weather的原型對象上,供實例使用 // 通過Weather實例調(diào)用changeWeather時,changeWeather中的this就是Weather實例 console.log(this) //undefined // 因為changeWeather不是通過實例調(diào)用的,是直接調(diào)用,所以this不會指向 實例對象 // 那么 changeWeather 的this 是指向 undefined還是 window呢 // 是 undefined 因為 類中的局部函數(shù)默認開啟了嚴格模式(類自動開的,與babel無關(guān)),所以不能指向 window // 獲取原來的isHot值 const isHot = this.state.isHot // 嚴重注意:狀態(tài)(state)里的數(shù)據(jù)不能直接進行更改,下面這行就是直接更改,react不認可?。?! // this.state.isHot = !isHot 錯誤寫法 // 嚴重注意:狀態(tài)必須通過setState進行修改,并且修改是一種合并,不是替換,只修改了isHot,wind不會丟失 this.setState({ isHot: !isHot }) } } // 2.渲染組件到頁面 ReactDOM.render(<Weather />, document.getElementById('test')) </script>
1.2.4 在類式組件使用state的簡寫方式
<script type="text/babel"> // 1.創(chuàng)建組件 class Weather extends React.Component { // 初始化狀態(tài)時直接在類里面寫賦值語句 state = { isHot: true, wind: '微風(fēng)' } render() { const { isHot, wind } = this.state return <h1 onClick={this.changeWeather}>今天天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1> } // 自定義方法 ————在用類去創(chuàng)建一個組件時,組件里所有自定義的方法都要用 賦值語句+箭頭函數(shù) 的形式 changeWeather = () => { //箭頭函數(shù)沒有this,箭頭函數(shù)內(nèi)的this指向其外側(cè) const isHot = this.state.isHot this.setState({ isHot: !isHot }) console.log(this) } } ReactDOM.render(<Weather />, document.getElementById('test')) </script>
1.2.3 強烈注意
- 組件中
render方法
中的this
為組件實例對象
- 組件
自定義的方法
中this為undefined
,如何解決?
①強制綁定this:通過函數(shù)對象的
bind()
②箭頭函數(shù)()=>{}
狀態(tài)數(shù)據(jù),不能直接修改或更新,狀態(tài)必須通過setState進行修改
1.3 組件實例的三大核心屬性之一:props
1.3.1 理解
- 每個組件對象都會有props(properties 的簡寫)屬性
組件標簽
的所有屬性
都保存在props
中 1.3.2 案例
需求: 自定義用來顯示一個人員信息的組件
- 姓名必須指定,且為字符串類型
- 性別為字符串類型,如果性別沒有指定,默認為男
- 年齡為字符串類型,且為數(shù)字類型,默認值為18
效果如下:
1.3.3 作用
- 通過
標簽屬性
從組件外向組件內(nèi)傳遞變化的數(shù)據(jù) - 注意:
組件內(nèi)部不要修改props數(shù)據(jù)
,因為props是只讀的
1.3.4 在類式組件使用props
先從內(nèi)部讀取某個屬性值:
//實例對象身上有個屬性props,需要傳值進去,那怎么傳呢?html標簽?zāi)軐憳撕瀸傩裕╧ey:value),那么組件標簽(<Person/>)也能寫屬性 // 解構(gòu)賦值 提前從props身上拿到這三個屬性 const { name, age, sex } = this.props
對props中的屬性值進行類型限制和必要性限制:
第一種方式(React v15.5 開始已棄用):
Person.propTypes = { name: React.PropTypes.string.isRequired, age: React.PropTypes.number }
第二種方式(新):使用prop-types庫進限制(需要引入prop-types庫)
//給Person加上propTypes屬性,react就能幫你限制了 //寫在Person類外面 Person.propTypes = { // 具體的propTypes規(guī)則,要去PropTypes(React里面內(nèi)置的一個屬性)里面找 name: PropTypes.string.isRequired, //限制name必傳,且為字符串 age: PropTypes.number //限制age為數(shù)值 }
在類式組件使用props的簡寫方式
// 用static表示給類自身加上一個propTypes和defaultProps屬性,而不是給類的實例對象加屬性 // 寫在Person類里面 static propTypes = { name: PropTypes.string.isRequired, //限制name必傳,且為字符串 sex: PropTypes.string, //限制sex為字符串 age: PropTypes.number, //限制age為數(shù)值 } static defaultProps = { sex: '男', //sex默認值為不男不女 age: 18 //age默認值為18 }
擴展屬性: 將對象的所有屬性
通過props傳遞
// 展開運算符在對對象使用時,應(yīng)當注意以{}包裹起來 <Person {...person}/> //...展開運算符具體運用看vscode
默認屬性值:
Person.defaultProps = { age: 18, //age默認值為18 sex:'男'//sex默認值為男 }
組件類的構(gòu)造函數(shù):
//開發(fā)中很少寫構(gòu)造器,能省則省 // 構(gòu)造器是否接收props,是否傳遞給super,取決于:是否希望在構(gòu)造器中通過this訪問props constructor(props){ // 只要寫了構(gòu)造器,就一定要調(diào)用super(),一定要傳props super(props) console.log(props)//打印所有屬性 }
1.3.5 在函數(shù)式組件使用props
<script type="text/babel"> // 創(chuàng)建組件 //函數(shù)式組件能接收參數(shù) function Person(props) { // console.log(props) const { name, sex, age } = props return ( <ul> <li>姓名:{name}</li> <li>性別:{sex}</li> <li>年齡:{age}</li> </ul> ) } Person.propTypes = { name: PropTypes.string.isRequired, //限制name必傳,且為字符串 sex: PropTypes.string, //限制sex為字符串 age: PropTypes.number, //限制age為數(shù)值 } Person.defaultProps = { sex: '男', //sex默認值為不男不女 age: 18 //age默認值為18 } ReactDOM.render(<Person name='旭旭' />, document.getElementById('test')) </script>
1.4 組件實例的三大核心屬性之一:refs與事件處理
1.4.1 理解
組件內(nèi)的標簽可以定義ref屬性
來標識自己(相當于原生里是id)
1.4.2 效果
需求: 自定義組件, 功能說明如下:
- 點擊按鈕, 提示第一個輸入框中的值
- 當?shù)?個輸入框失去焦點時, 提示這個輸入框中的值
1.4.3 字符串形式的ref
<script type="text/babel"> // 創(chuàng)建組件 class Demo extends React.Component { // 展示左側(cè)輸入框的數(shù)據(jù) showData = () => { const { input1 } = this.refs alert(input1.value) } // 展示右側(cè)輸入框的數(shù)據(jù) showData2 = () => { const { input2 } = this.refs alert(input2.value) } render() { return ( <div> <input ref="input1" type="text" placeholder="點擊按鈕提示數(shù)據(jù)" /> <button onClick={this.showData}>點我提示左側(cè)的數(shù)據(jù)</button> <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦點提示數(shù)據(jù)" /> </div> ) } } //渲染組件 ReactDOM.render(<Demo />, document.getElementById('test')) </script>
過時的API:String 類型的 Refs
如果你之前使用過 React,你可能了解過之前的 API 中的 string 類型的 ref 屬性,例如
"textInput"
。你可以通過this.refs.textInput
來訪問 DOM 節(jié)點。我們不建議使用它,因為 string 類型的 refs 存在 一些問題。它已過時并可能會在未來的版本被移除。
效果如下:
1.4.4 回調(diào)形式的ref
<script type="text/babel"> // 創(chuàng)建組件 class Demo extends React.Component { // 展示左側(cè)輸入框的數(shù)據(jù) showData = () => { const { input1 } = this alert(input1.value) } // 展示右側(cè)輸入框的數(shù)據(jù) showData2 = () => { const { input2 } = this alert(input2.value) } // 回調(diào)函數(shù)的特點:你定義的,別人調(diào)的,最終執(zhí)行了 // 回調(diào)函數(shù)的參數(shù)正是ref當前所處的那個input節(jié)點 render() { return ( <div> {/* 代碼執(zhí)行步驟: */} {/* React加載Demo組件時,執(zhí)行render函數(shù)內(nèi)的jsx代碼,發(fā)現(xiàn)input中有ref屬性,屬性內(nèi)容是一個箭頭函數(shù),React就會幫我們調(diào)用這個回調(diào)函數(shù),并且把當前的DOM傳進這個函數(shù),這樣就可以接收到當前的DOM節(jié)點了,并把這個DOM放在組件實例自身上 */} {/* 箭頭函數(shù)接收到參數(shù)(當前的DOM節(jié)點)命名為(currentNode),將currentNode賦值給實例對象下的input1這個屬性 */} {/* 箭頭函數(shù)只有一個參數(shù)可以省略'()',箭頭函數(shù)右邊只有一條函數(shù)體可以省略'{}' */} {/*<input ref={currentNode => this.input1 = currentNode} type="<text>" placeholder="點擊按鈕提示數(shù)據(jù)" /> */} <input ref={(currentNode) => { this.input1 = currentNode }} type="text" placeholder="點擊按鈕提示數(shù)據(jù)" /> <button onClick={this.showData}>點我提示左側(cè)的數(shù)據(jù)</button> <input ref={(currentNode) => { this.input2 = currentNode }} onBlur={this.showData2} type="text" placeholder="失去焦點提示數(shù)據(jù)" /> </div> ) } } //渲染組件 ReactDOM.render(<Demo />, document.getElementById('test')) </script>
效果如下:
1.4.5 回調(diào)ref中回調(diào)執(zhí)行次數(shù)的問題(class的綁定函數(shù))
<script type="text/babel"> // 創(chuàng)建組件 class Demo extends React.Component { state = { isHot: true } showInfo = () => { const { input1 } = this alert(input1.value) } changeWeather = () => { // 獲取原來的狀態(tài) const { isHot } = this.state // 更新狀態(tài) this.setState({ isHot: !isHot }) } savaInput = (currentNode) => { this.input1 = currentNode; } render() { const { isHot } = this.state return ( <div> <h1>今天天氣很{isHot ? '炎熱' : '涼爽'}</h1> {/* React在更新組件時,會先傳入null調(diào)用一次ref中的回調(diào),以清空之前的ref;然后再傳入?yún)?shù)currentNode,以調(diào)用第二次。每更新一次組件,ref中的回調(diào)函數(shù)就會被調(diào)用兩次,一次傳入null,一次傳入?yún)?shù)currentNode。為了應(yīng)對這種情況的出現(xiàn),官方建議將ref的回調(diào)函數(shù)定義成 class 的綁定函數(shù) 的方式去避免上述的問題。 */} {/*內(nèi)聯(lián)函數(shù)*/} {/*<input ref={(currentNode) => { this.input1 = currentNode; console.log('@', currentNode) }} type="text" /><br /><br />*/} <input ref={this.savaInput} type="text" /><br /><br /> <button onClick={this.showInfo}>點我提示輸入的數(shù)據(jù)</button> <button onClick={this.changeWeather}>點我切換天氣</button> </div> ) } } //渲染組件 ReactDOM.render(<Demo />, document.getElementById('test')) </script>
React在更新組件時,會先傳入null調(diào)用一次ref中的回調(diào),以清空之前的ref;然后再傳入?yún)?shù)currentNode,以調(diào)用第二次。每更新一次組件,ref中的回調(diào)函數(shù)就會被調(diào)用兩次,一次傳入null,一次傳入?yún)?shù)currentNode。為了應(yīng)對這種情況的出現(xiàn),官方建議將ref的回調(diào)函數(shù)定義成
class 的綁定函數(shù)
的方式去避免上述的問題。
效果如下:
1.4.6 createRef創(chuàng)建ref容器
<script type="text/babel"> // 創(chuàng)建組件 class Demo extends React.Component { myRef = React.createRef() myRef2 = React.createRef() /* React.createRef調(diào)用后可以返回一個容器,該容器可以存儲被ref所標識的節(jié)點,該容器是“專人專用”的,也就是,調(diào)用React.createRef()創(chuàng)建了一個容器,通過賦值語句賦值給實例自身名為myRef的屬性上 */ // 展示左側(cè)輸入框的數(shù)據(jù) showData = () => { // console.log(this.myRef) alert(this.myRef.current.value) } // 展示右側(cè)輸入框的數(shù)據(jù) showData2 = () => { alert(this.myRef2.current.value) } // 回調(diào)函數(shù)的特點:你定義的,別人調(diào)的,最終執(zhí)行了 // 回調(diào)函數(shù)的參數(shù)正是ref當前所處的那個input節(jié)點 render() { return ( <div> {/* React執(zhí)行render函數(shù)中的jsx代碼時, 發(fā)現(xiàn)input上有一個ref屬性而且是通過createRef方法創(chuàng)建的, React就會把當前ref屬性所在的DOM節(jié)點放到之前創(chuàng)建的那個容器上, 也就是把當前input這個DOM節(jié)點放到了實例自身名為myRef的容器上 */} {/* 可以簡寫成ref={this.myRef = React.createRef()} */} <input ref={this.myRef} type="text" placeholder="點擊按鈕提示數(shù)據(jù)" /> <button onClick={this.showData}>點我提示左側(cè)的數(shù)據(jù)</button> {/* 發(fā)生事件的元素正好是需要操作的元素本身,可以省略ref */} <input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦點提示數(shù)據(jù)" /> </div> ) } } //渲染組件 ReactDOM.render(<Demo />, document.getElementById('test')) </script>
效果如下:
1.4.7 事件處理
- 通過
onXxx屬性
指定事件處理函數(shù)(注意大小寫
) - React使用的是自定義(合成)事件, 而不是使用的原生DOM事件-------為了更好的兼容性
- React中的事件是通過事件委托方式處理的(委托給組件最外層的元素)--------為了高效
通過event.target
得到發(fā)生事件的DOM元素對象------不要過度使用ref
<script type="text/babel"> // 創(chuàng)建一個組件 class Demo extends React.Component { showInfo = () => { alert(this.myRef.current.value) } showData = (event) => { // 傳入的event是發(fā)生onBlur事件的事件源,也就是失去焦點提示數(shù)據(jù)的input框, // 通過event.target.value拿到input中的值 alert(event.target.value) } render() { return ( <div> <input ref={this.myRef = React.createRef()} type="text" placeholder="點擊按鈕提示數(shù)據(jù)" /> <button onClick={this.showInfo}>點我提示左側(cè)數(shù)據(jù)</button> <input onBlur={this.showData} type="text" placeholder="失去焦點提示數(shù)據(jù)" /> </div> ) } } // 渲染組件到頁面 ReactDOM.render(<Demo />, document.getElementById('test')) </script>
1.5 收集表單數(shù)據(jù)
1.5.1 理解
包含表單的組件分類
- 受控組件
- 非受控組件
1.5.2 案例
需求: 定義一個包含表單的組件
輸入用戶名密碼后, 點擊登錄提示輸入信息
效果如下:
Ⅰ.非受控組件
對input(以及頁面中其他的一些輸入型DOM)中輸入的數(shù)據(jù)
現(xiàn)用現(xiàn)取
,就是非受控組件
<script type="text/babel"> // 創(chuàng)建一個組件 class Login extends React.Component { // handleSubmit函數(shù)中直接取出input中輸入的數(shù)據(jù)(現(xiàn)用現(xiàn)?。? handleSubmit = (event) => { event.preventDefault() //阻止表單提交 const { username, password } = this alert(`你輸入的用戶名是: ${username.value}, 你輸入的密碼是: ${password.value}`) } render() { return ( // 對input(以及其他的一些輸入型DOM)中輸入的數(shù)據(jù)現(xiàn)用現(xiàn)取,就是非受控組件 <form onSubmit={this.handleSubmit}> 用戶名:<input ref={(c) => { this.username = c }} type="text" name="username" /> 密碼:<input ref={(c) => { this.password = c }} type="password" name="password" /> {/* form中的button默認提交,點擊后觸發(fā)表單提交事件onSubmit */} <button>登錄</button> </form> ) } } // 渲染組件到頁面 ReactDOM.render(<Login />, document.getElementById('test')) </script>
Ⅱ.受控組件
頁面中所有輸入類的DOM(input框),隨著用戶的輸入,能把輸入的東西維護到狀態(tài)里(
state
),等需要用時,直接從狀態(tài)里面取出來,就是受控組件
。(類似于vue里的雙向綁定
)
<script type="text/babel"> // 創(chuàng)建一個組件 class Login extends React.Component { // 初始化狀態(tài) state = { username: '', password: '' } // 保存用戶名到狀態(tài)中 saveUsername = (event) => { this.setState({ username: event.target.value }) } // 保存密碼到狀態(tài)中 savePassword = (event) => { this.setState({ password: event.target.value }) } // 表單提交的回調(diào) handleSubmit = (event) => { event.preventDefault() //阻止表單提交 const { username, password } = this.state alert(`你輸入的用戶名是: ${username}, 你輸入的密碼是: ${password}`) } /* 受控組件:(能夠省略ref) 頁面中所有輸入類的DOM(input框),隨著用戶的輸入,能把輸入的東西維護到狀態(tài)里(state),等需要用時,直接從狀態(tài)里面取出來 數(shù)據(jù)被onChange函數(shù)監(jiān)聽,只要輸入型DOM數(shù)據(jù)一改變就觸發(fā)onChange中指定的回調(diào)函數(shù) 回調(diào)函數(shù)saveUsername和savePassword中對改變的數(shù)據(jù)進行保存,保存到state中,需要使用的時候才取出。 */ render() { return ( <form onSubmit={this.handleSubmit}> 用戶名:<input onChange={this.saveUsername} type="text" name="username" /> 密碼:<input onChange={this.savePassword} type="password" name="password" /> <button>登錄</button> </form> ) } } // 渲染組件到頁面 ReactDOM.render(<Login />, document.getElementById('test')) </script>
到此這篇關(guān)于React組件三大屬性之state,props,refs的文章就介紹到這了,更多相關(guān)React組件屬性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Electron整合React使用搭建開發(fā)環(huán)境的步驟詳解
這篇文章主要介紹了Electron整合React使用搭建開發(fā)環(huán)境,本文分步驟給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值 ,需要的朋友可以參考下2020-06-06react使用useState修改對象或者數(shù)組的值無法改變視圖的問題
這篇文章主要介紹了react使用useState修改對象或者數(shù)組的值無法改變視圖的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08更強大的React 狀態(tài)管理庫Zustand使用詳解
這篇文章主要為大家介紹了更強大的React 狀態(tài)管理庫Zustand使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10如何解決React官方腳手架不支持Less的問題(小結(jié))
這篇文章主要介紹了如何解決React官方腳手架不支持Less的問題(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實現(xiàn)
本文主要介紹了React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11