動(dòng)態(tài)h5網(wǎng)站開(kāi)發(fā)百度指數(shù)分析數(shù)據(jù)
本文來(lái)自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)
一. refs 的使用
在React的開(kāi)發(fā)模式中,通常情況下不需要、也不建議直接操作DOM原生,但是某些特殊的情況,確實(shí)需要獲取到DOM進(jìn)行某些操作:
- 管理焦點(diǎn),文本選擇或媒體播放。
- 觸發(fā)強(qiáng)制動(dòng)畫(huà)。
- 集成第三方 DOM 庫(kù)。
1.1. 創(chuàng)建 ref 的方式
如何創(chuàng)建refs
來(lái)獲取對(duì)應(yīng)的DOM呢?目前有三種方式:
- 方式一:傳入字符串
- 使用時(shí)通過(guò)
this.refs.傳入的字符串
格式獲取對(duì)應(yīng)的元素;
- 使用時(shí)通過(guò)
- 方式二:傳入一個(gè)對(duì)象
- 對(duì)象是通過(guò)
React.createRef()
方式創(chuàng)建出來(lái)的; - 使用時(shí)獲取到創(chuàng)建的對(duì)象其中有一個(gè)
current
屬性就是對(duì)應(yīng)的元素;
- 對(duì)象是通過(guò)
- 方式三:傳入一個(gè)函數(shù)
- 該函數(shù)會(huì)在DOM被掛載時(shí)進(jìn)行回調(diào),這個(gè)函數(shù)會(huì)傳入一個(gè) 元素對(duì)象,我們可以自己保存;
- 使用時(shí),直接拿到之前保存的元素對(duì)象即可;
代碼演練:
import React, { PureComponent, createRef } from 'react'export default class App extends PureComponent {constructor(props) {super(props);this.titleRef = createRef();this.titleEl = null;}render() {return (<div><h2 ref="title">String Ref</h2><h2 ref={this.titleRef}>Hello Create Ref</h2><h2 ref={element => this.titleEl = element}>Callback Ref</h2><button onClick={e => this.changeText()}>改變文本</button></div>)}changeText() {this.refs.title.innerHTML = "你好啊,李銀河";this.titleRef.current.innerHTML = "你好啊,李銀河";this.titleEl.innerHTML = "你好啊,李銀河";}
}
1.2. ref 節(jié)點(diǎn)的類型
ref
的值根據(jù)節(jié)點(diǎn)的類型而有所不同:
- 當(dāng)
ref
屬性用于 HTML 元素時(shí),構(gòu)造函數(shù)中使用React.createRef()
創(chuàng)建的ref
接收底層 DOM 元素作為其current
屬性; - 當(dāng)
ref
屬性用于自定義class
組件時(shí),ref
對(duì)象接收組件的掛載實(shí)例作為其current
屬性; - 你不能在函數(shù)組件上使用
ref
屬性,因?yàn)樗麄儧](méi)有實(shí)例;
這里我們演示一下ref
引用一個(gè)class
組件對(duì)象:
import React, { PureComponent, createRef } from 'react';class Counter extends PureComponent {constructor(props) {super(props);this.state = {counter: 0}}render() {return (<div><h2>當(dāng)前計(jì)數(shù): {this.state.counter}</h2><button onClick={e => this.increment()}>+1</button></div>)}increment() {this.setState({counter: this.state.counter + 1})}
}export default class App extends PureComponent {constructor(props) {super(props);this.counterRef = createRef();}render() {return (<div><Counter ref={this.counterRef}/><button onClick={e => this.increment()}>app +1</button></div>)}increment() {this.counterRef.current.increment();}
}
函數(shù)式組件是沒(méi)有實(shí)例的,所以無(wú)法通過(guò)ref
獲取他們的實(shí)例:
- 但是某些時(shí)候,我們可能想要獲取函數(shù)式組件中的某個(gè)DOM元素;
- 這個(gè)時(shí)候我們可以通過(guò)
React.forwardRef
,后面我們也會(huì)學(xué)習(xí)hooks
中如何使用ref
;
1.3 ref 轉(zhuǎn)發(fā)
import React, { PureComponent, createRef } from 'react';function Home(props) {return (<div><h2 ref={props.ref}>Home</h2><button>按鈕</button></div>)
}export default class App extends PureComponent {constructor(props) {super(props);this.homeTitleRef = createRef();}render() {return (<div><Home ref={this.homeTitleRef}/><button onClick={e => this.printInfo()}>打印ref</button></div>)}printInfo() {console.log(this.homeTitleRef);}
}
使用forwardRef
import React, { PureComponent, createRef, forwardRef } from 'react';const Home = forwardRef(function(props, ref) {return (<div><h2 ref={ref}>Home</h2><button>按鈕</button></div>)
})export default class App extends PureComponent {constructor(props) {super(props);this.homeTitleRef = createRef();}render() {return (<div><Home ref={this.homeTitleRef}/><button onClick={e => this.printInfo()}>打印ref</button></div>)}printInfo() {console.log(this.homeTitleRef.current);}
}
二. 受控組件
2.1. 認(rèn)識(shí)受控組件
2.1.1. 默認(rèn)提交表單方式
在React中,HTML表單的處理方式和普通的DOM元素不太一樣:表單元素通常會(huì)保存在一些內(nèi)部的state
。
比如下面的HTML表單元素:
<form><label>名字:<input type="text" name="name" /></label><input type="submit" value="提交" />
</form>
- 這個(gè)處理方式是DOM默認(rèn)處理HTML表單的行為,在用戶點(diǎn)擊提交時(shí)會(huì)提交到某個(gè)服務(wù)器中,并且刷新頁(yè)面;
- 在React中,并沒(méi)有禁止這個(gè)行為,它依然是有效的;
- 但是通常情況下會(huì)使用JavaScript函數(shù)來(lái)方便的處理表單提交,同時(shí)還可以訪問(wèn)用戶填寫的表單數(shù)據(jù);
- 實(shí)現(xiàn)這種效果的標(biāo)準(zhǔn)方式是使用“受控組件”;
2.1.2. 受控組件提交表單
在 HTML 中,表單元素(如<input>
、 <textarea>
和 <select>
)之類的表單元素通常自己維護(hù) state
,并根據(jù)用戶輸入進(jìn)行更新。
而在 React 中,可變狀態(tài)(mutable state
)通常保存在組件的 state
屬性中,并且只能通過(guò)使用 setState()
來(lái)更新。
- 我們將兩者結(jié)合起來(lái),使React的
state
成為“唯一數(shù)據(jù)源”; - 渲染表單的 React 組件還控制著用戶輸入過(guò)程中表單發(fā)生的操作;
- 被 React 以這種方式控制取值的表單輸入元素就叫做“受控組件”;
例如,如果我們想讓前一個(gè)示例在提交時(shí)打印出名稱,我們可以將表單寫為受控組件:
import React, { PureComponent } from 'react'export default class App extends PureComponent {constructor(props) {super(props);this.state = {username: ""}}render() {const {username} = this.state;return (<div><form onSubmit={e => this.handleSubmit(e)}><label htmlFor="username">用戶名: <input type="text" id="username" onChange={e => this.handleUsernameChange(e)} value={username}/></label><input type="submit" value="提交"/></form></div>)}handleUsernameChange(event) {this.setState({username: event.target.value})}handleSubmit(event) {console.log(this.state.username);event.preventDefault();}
}
由于在表單元素上設(shè)置了 value
屬性,因此顯示的值將始終為 this.state.value
,這使得 React 的 state
成為唯一數(shù)據(jù)源。
由于 handleUsernameChange
在每次按鍵時(shí)都會(huì)執(zhí)行并更新 React 的 state
,因此顯示的值將隨著用戶輸入而更新。
2.2. 常見(jiàn)表單的處理
剛才我們演示的是一個(gè)input
表單的處理,這里我們?cè)傺菔疽幌缕渌那闆r。
2.2.1. textarea標(biāo)簽
texteare
標(biāo)簽和input
比較相似:
import React, { PureComponent } from 'react'export default class App extends PureComponent {constructor(props) {super(props);this.state = {article: "請(qǐng)編寫你喜歡的文章"}}render() {return (<div><form onSubmit={e => this.handleSubmit(e)}><label htmlFor="article"><textarea id="article" cols="30" rows="10"value={this.state.article}onChange={e => this.handleArticelChange(e)}/></label><div><input type="submit" value="發(fā)布文章"/></div></form></div>)}handleArticelChange(event) {this.setState({article: event.target.value})}handleSubmit(event) {console.log(this.state.article);event.preventDefault();}
}
2.2.2. select標(biāo)簽
select
標(biāo)簽的使用也非常簡(jiǎn)單,只是它不需要通過(guò)selected
屬性來(lái)控制哪一個(gè)被選中,它可以匹配state
的value
來(lái)選中。
我們來(lái)進(jìn)行一個(gè)簡(jiǎn)單的演示:
import React, { PureComponent } from 'react'export default class App extends PureComponent {constructor(props) {super(props);this.state = {fruits: "orange"}}render() {return (<div><form onSubmit={e => this.handleSubmit(e)}><label htmlFor="fruits"><select id="fruits" value={this.state.fruits}onChange={e => this.handleFruitsChange(e)}><option value="apple">蘋果</option><option value="orange">橘子</option><option value="banana">香蕉</option></select></label><div><input type="submit" value="提交"/></div></form></div>)}handleFruitsChange(event) {this.setState({fruits: event.target.value})}handleSubmit(event) {console.log(this.state.article);event.preventDefault();}
}
2.2.3. 處理多個(gè)輸入
多處理方式可以像單處理方式那樣進(jìn)行操作,但是需要多個(gè)監(jiān)聽(tīng)方法:
- 這里我們可以使用ES6的一個(gè)語(yǔ)法:計(jì)算屬性名(Computed property names)
let i = 0
let a = {['foo' + ++i]: i,['foo' + ++i]: i,['foo' + ++i]: i
}console.log(a.foo1) // 1
console.log(a.foo2) // 2
console.log(a.foo3) // 3let param = 'size'
let config = {[param]: 12,['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4
}console.log(config) // {size: 12, mobileSize: 4}
我們進(jìn)行對(duì)應(yīng)的代碼演練:
import React, { PureComponent } from 'react'export default class App extends PureComponent {constructor(props) {super(props);this.state = {username: "",password: ""}}render() {const {username, password} = this.state;return (<div><form onSubmit={e => this.handleSubmit(e)}><label htmlFor="username">用戶: <input type="text" id="username" name="username"onChange={e => this.handleChange(e)} value={username}/></label><label htmlFor="password">密碼: <input type="text" id="password" name="password"onChange={e => this.handleChange(e)} value={password}/></label><input type="submit" value="提交"/></form></div>)}handleChange(event) {this.setState({[event.target.name]: event.target.value})}handleSubmit(event) {console.log(this.state.username, this.state.password);event.preventDefault();}
}
三. 非受控組件
React推薦大多數(shù)情況下使用 受控組件 來(lái)處理表單數(shù)據(jù):
- 一個(gè)受控組件中,表單數(shù)據(jù)是由 React 組件來(lái)管理的;
- 另一種替代方案是使用非受控組件,這時(shí)表單數(shù)據(jù)將交由 DOM 節(jié)點(diǎn)來(lái)處理;
如果要使用非受控組件中的數(shù)據(jù),那么我們需要使用 ref
來(lái)從DOM節(jié)點(diǎn)中獲取表單數(shù)據(jù)。
我們來(lái)進(jìn)行一個(gè)簡(jiǎn)單的演練:
- 使用
ref
來(lái)獲取input
元素; - 在非受控組件中通常使用
defaultValue
來(lái)設(shè)置默認(rèn)值;
import React, { PureComponent, createRef } from 'react'export default class App extends PureComponent {constructor(props) {super(props);this.usernameRef = createRef();}render() {return (<div><form onSubmit={e => this.handleSubmit(e)}><label htmlFor="">用戶:<input defaultValue="username" type="text" name="username" ref={this.usernameRef}/></label><input type="submit" value="提交"/></form></div>)}handleSubmit(event) {event.preventDefault();console.log(this.usernameRef.current.value);}
}
同樣,<input type="checkbox">
和 <input type="radio">
支持 defaultChecked
,<select>
和 <textarea>
支持 defaultValue
。