React ref的使用示例
寫了一段時(shí)間的 react,99%都在寫 state、prop、useState、useEffect,對 ref 特別不熟悉,前幾天做一個(gè)需求,想用 ref 實(shí)現(xiàn)父組件撈子組件的某個(gè)狀態(tài)值,結(jié)果失敗了,特此整理一下 ref 相關(guān)內(nèi)容。
什么是 ref
官網(wǎng)介紹:
在典型的 React 數(shù)據(jù)流中,props 是父組件與子組件交互的唯一方式。要修改一個(gè)子組件,你需要使用新的 props 來重新渲染它。但是,在某些情況下,你需要在典型數(shù)據(jù)流之外強(qiáng)制修改子組件。被修改的子組件可能是一個(gè) React 組件的實(shí)例,也可能是一個(gè) DOM 元素。對于這兩種情況,React 都提供了解決辦法,即使用 ref 來獲取 dom 或組件實(shí)例。
如何使用 ref
放在 dom 元素上
這是 ref 最直接的用法
export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } componentDidMount() { console.log(this.myRef) } render() { return <div ref={this.myRef}>測試</div> } }
打印看一下 ref 是啥
可以看出,ref.current 拿到了 dom 元素,所以我們可以實(shí)現(xiàn) dom 元素本身的一些功能,如 input 的聚焦:
export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } onClick = () => { this.myRef.current.focus() } render() { return ( <div> <button onClick={this.onClick}>聚焦</button> <input ref={this.myRef} /> </div> ) } }
官網(wǎng)還提供了一種 ref 回調(diào)的形式:
export class Demo extends React.Component { constructor(props) { super(props) this.myRef = null } onClick = () => { this.myRef.focus() } render() { return ( <div> <button onClick={this.onClick}>聚焦</button> <input ref={ele => this.myRef = ele} /> // 這里的 ele 就是該 dom 元素 </div> ) } }
放在類組件上
其實(shí)組件跟原生 dom 差不多,也是擁有自己的 ui、一些功能的某種元素,所以將 ref 放在組件上,也可以獲取到該組件的示例。
// 子組件 class Child extends React.Component { constructor(props) { super(props) this.state = { name: 'xx' } } render() { return <div>子元素{this.state.name}</div> } } export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } componentDidMount() { console.log(this.myRef) } render() { return ( <Child ref={this.myRef} /> ) } }
那既然可以獲取到子組件的實(shí)例,我們就可以操作子組件了,比如文章最開始說,我想在父組件里去撈子組件的某些狀態(tài)值。
class Child extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } onClick = () => { this.setState({count: this.state.count+1}) } render() { return <button onClick={this.onClick}>點(diǎn)擊+1:{this.state.count}</button> } } export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } onClick = () => { console.log(this.myRef.current.state.count) // 拿到子組件的狀態(tài)值 } render() { return ( <div> <button onClick={this.onClick}>獲取子組件的點(diǎn)擊次數(shù)</button> <Child ref={this.myRef} /> // ref 獲取到子組件實(shí)例 </div> ) } }
既然能拿值,我也能拿函數(shù)去修改子組件
class Child extends React.Component { constructor(props) { super(props) this.state = { name: 'xx' } } changeName = () => { this.setState({name: 'ww'}) } render() { return <div>子元素{this.state.name}</div> } } export class Demo extends React.Component { constructor(props) { super(props) this.myRef = createRef() } onClick = () => { this.myRef.current.changeName() // 父組件的手伸到子組件里去啦 } render() { return ( <div> <button onClick={this.onClick}>改變子組件的狀態(tài)</button> <Child ref={this.myRef} /> </div> ) } }
當(dāng)然這個(gè)例子并不恰當(dāng),父組件想更改子組件的狀態(tài)的話,應(yīng)該把狀態(tài)提升到父組件中,然后作為子組件的props傳遞進(jìn)去。
主要是 ref 提供一種方式去繞過 props 來實(shí)現(xiàn)父子組件通信。
放在函數(shù)組件上
這是我文章開頭寫需求時(shí)犯的錯(cuò),ref 不能放在函數(shù)組件上,因?yàn)楹瘮?shù)組件沒有實(shí)例。
const Child = () => { return <div>子組件</div> } export const Demo = () => { const myRef = useRef() // 可以在函數(shù)組件內(nèi)創(chuàng)建 ref useEffect(() => { console.log(myRef) }, []) return <Child ref={myRef} /> // 但是放在函數(shù)組件上無效 }
那函數(shù)組件就不能使用 ref 了嗎,那肯定不是哈哈。我們可以使用 forwardRef 包裝函數(shù)組件。
const Child = (props, ref) => { // 包裝后,除了原有的 props 外, ref 也被傳了進(jìn)來 return <div ref={ref}>子組件</div> // 還是得掛載到 dom 上 } const ProChild = React.forwardRef(Child) // 重點(diǎn)在這里 export const Demo = () => { const myRef = useRef() useEffect(() => { console.log(myRef) }, []) return <ProChild ref={myRef} /> }
這里貼一下官網(wǎng)的 tip:
那既然函數(shù)組件也可以使用 ref 的話,我們用函數(shù)組件實(shí)現(xiàn)一下父組件撈子組件的數(shù)據(jù),不過可以看出,使用 forwardRef 包裹后,ref 還是得掛載到 dom 或者類組件上,如果我只想掛載數(shù)據(jù)還需要搭配 useImperativeHandle。
const Child = (props, ref) => { const [count, setCount] = useState(0) useImperativeHandle( ref, () => ({ // 這里就是暴露給外部 ref 的數(shù)據(jù) getVal: ()=> count }), [count], ) const onClick = () => { setCount(pre => pre+1) } return <button onClick={onClick}>點(diǎn)擊+1:{count}</button> } const ProChild = React.forwardRef(Child) export const Demo = () => { const myRef = useRef() const onClick = () => { console.log(myRef.current.getVal()) // 拿到子組件的值 } return <><button onClick={onClick}>獲取子組件的點(diǎn)擊次數(shù)</button><ProChild ref={myRef} /></> }
至此完成了做需求時(shí)留下的問題 ✅
總結(jié)
最后還是需要強(qiáng)調(diào)一下,父組件獲取子組件狀態(tài)的場景,一般還是狀態(tài)提升 + 回調(diào)來通信,需求最終也是使用這種方式來實(shí)現(xiàn)的,最開始之所以想用 ref,是覺得狀態(tài)提升后,子組件變化了會(huì)引起父組件的重新渲染,但是我只想拿數(shù)據(jù)而不引起渲染。
跟師傅說了一下我寫需求時(shí)的想法,師傅見解如下:
- 優(yōu)先考慮狀態(tài)提升
- 有性能問題的話,考慮狀態(tài)提升 + memo
- 不想給多個(gè)組件加 memo 的話,就要考慮引入 redux/mobx 了
- 如果引入 redux/mobx 是一種成本的話,那 ref 也不是不可以哈哈哈
以上就是React ref的使用詳解的詳細(xì)內(nèi)容,更多關(guān)于React ref的使用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
react路由基礎(chǔ)解讀(Router、Link和Route)
這篇文章主要介紹了react路由基礎(chǔ)解讀(Router、Link和Route),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07concent漸進(jìn)式重構(gòu)react應(yīng)用使用詳解
這篇文章主要為大家介紹了concent漸進(jìn)式重構(gòu)react應(yīng)用的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11React Native按鈕Touchable系列組件使用教程示例
這篇文章主要為大家介紹了React Native按鈕Touchable系列組件使用教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11解決React報(bào)錯(cuò)JSX?element?type?does?not?have?any?construct
這篇文章主要為大家介紹了解決React報(bào)錯(cuò)JSX?element?type?does?not?have?any?construct?or?call?signatures,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12React 使用recharts實(shí)現(xiàn)散點(diǎn)地圖的示例代碼
這篇文章主要介紹了React 使用recharts實(shí)現(xiàn)散點(diǎn)地圖的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12react router4+redux實(shí)現(xiàn)路由權(quán)限控制的方法
本篇文章主要介紹了react router4+redux實(shí)現(xiàn)路由權(quán)限控制的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05React?component.forceUpdate()強(qiáng)制重新渲染方式
這篇文章主要介紹了React?component.forceUpdate()強(qiáng)制重新渲染方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過濾器
這篇文章主要介紹了如何去除富文本中的html標(biāo)簽及vue、react、微信小程序中的過濾器,在vue及react中經(jīng)常會(huì)遇到,今天通過實(shí)例代碼給大家講解,需要的朋友可以參考下2018-11-11