ref用法

目录

React中提供两种方法创建ref对象:

类组件获取 Ref 三种方式

① Ref属性是一个字符串。

② Ref 属性是一个函数。

③ Ref属性是一个ref对象。

高级用法1:forwardRef 转发 Ref

场景一:跨层级获取

场景二: 合并转发ref

场景三:高阶组件转发

高级用法2:ref实现组件通信

场景一:对于类组件可以通过 ref 直接获取组件实例,实现父 <-> 子 双向通信。

 场景二:函数组件 forwardRef + useImperativeHandle


【ref作用】:最熟悉的就是  用 Ref 获取真实 DOM 元素和获取类组件实例,除此之外的功能,ref 派生出一些其他的高级用法,能够解决一些特殊场景下的问题

React中提供两种方法创建ref对象:

        第一种: 类组件中  React.createRef 创建一个 ref 对象。

class Index extends React.Component{
    constructor(props){
       super(props)
       this.currentDom = React.createRef(null)
    }
    componentDidMount(){
        console.log(this.currentDom)
    }
    render= () => <div ref={ this.currentDom } >ref对象模式获取元素或组件</div>
}

        第二种:函数组件中  React.useRef 创建 Ref

export default function Index(){
    const currentDom = React.useRef(null)

    React.useEffect(()=>{
        console.log( currentDom.current ) // div
    },[])

    return  <div ref={ currentDom } >ref对象模式获取元素或组件</div>
}

【什么是 ref 对象,所谓 ref 对象就是用 createRef 或者 useRef 创建出来的对象】

【问:在函数组件中为什么不能用 createRef ?】
        答:类组件有一个实例 instance 能够维护像 ref 这种信息,但函数组件每次更新时所有的变量都会重新声明,此时 ref 就会随着函数组件执行被重置。


类组件获取 Ref 三种方式

  • ① Ref属性是一个字符串。
/* 类组件 */
class Children extends Component{  
    render=()=><div>hello,world</div>
}


/* TODO:  Ref属性是一个字符串 */
export default class Index extends React.Component{
    componentDidMount(){
       console.log(this.refs)
    }
    render=()=> <div>
        <div ref="currentDom"  >字符串模式获取元素或组件</div>
        <Children ref="currentComInstance"  />
    </div>
}

React 在底层逻辑,会判断类型,如果是 DOM 元素,会把真实 DOM 绑定在组件 this.refs (组件实例下的 refs )属性上,如果是类组件,会把子组件的实例绑定在 this.refs 上。(函数组件没有实例,不能被 Ref 标记)

  • ② Ref 属性是一个函数。
class Children extends React.Component{  
    render=()=><div>hello,world</div>
}

/* TODO: Ref属性是一个函数 */
export default class Index extends React.Component{
    currentDom = null
    currentComponentInstance = null

    componentDidMount(){
        console.log(this.currentDom)
        console.log(this.currentComponentInstance)
    }
    render=()=> <div>
        <div ref={(node)=> this.currentDom = node }  >Ref模式获取元素或组件</div>
        <Children ref={(node) => this.currentComponentInstance = node  }  />
    </div>
}

  • ③ Ref属性是一个ref对象。
class Children extends React.Component{  
    render=()=><div>hello,world</div>
}

export default class Index extends React.Component{
    currentDom = React.createRef(null)
    currentComponentInstance = React.createRef(null)

    componentDidMount(){
        console.log(this.currentDom)
        console.log(this.currentComponentInstance)
    }

    render=()=> <div>
         <div ref={ this.currentDom }  >Ref对象模式获取元素或组件</div>
        <Children ref={ this.currentComponentInstance }  />
   </div>
}

高级用法1:forwardRef 转发 Ref

forwardRef 的初衷就是解决 ref 不能跨层级捕获和传递的问题。 forwardRef 接受了父级元素标记的 ref 信息,并把它转发下去,使得子组件可以通过 props 来接受到上一层级或者是更上层级的ref,大家可能对我这句话不是很理解,不过没关系,下面来从具体场景中分析 forwardRef 的真正用途。

场景一:跨层级获取
场景二: 合并转发ref
场景三:高阶组件转发

高级用法2:ref实现组件通信
场景一:对于类组件可以通过 ref 直接获取组件实例,实现父 <-> 子 双向通信。

如果有种场景不想通过父组件 render 改变 props 的方式,来触发子组件的更新,也就是子组件通过 state 单独管理数据层,针对这种情况父组件可以通过 ref 模式标记子组件实例,从而操纵子组件方法,这种情况通常发生在一些数据层托管的组件上,比如 <Form/> 表单,经典案例可以参考 antd 里面的 form 表单,暴露出对外的 resetFields , setFieldsValue 等接口,可以通过表单实例调用这些 API 。

/* 子组件 */
class Son extends React.PureComponent{
    state={
       fatherMes:'',
       sonMes:''
    }

    // 1. 子组件暴露方法 fatherSay 供父组件使用,子组件拿到父组件传来的值,然后存到state中的fatherMes身上。
    fatherSay=(fatherMessage)=> this.setState({ fatherMes: fatherMessage}) /* 提供给父组件的API */

    render(){
        const { fatherMes, sonMes } = this.state

        return <div className="sonbox" >
            <div className="title" >子组件</div>

            // 2. 在这里应用父组件传来的值
            <p>父组件对我说:{ fatherMes }</p> 

            // 3. 把子组件输入框里的值存在state里定义的sonMes身上
            <div className="label" >对父组件说</div> <input  onChange={(e)=>this.setState({ sonMes:e.target.value })}   className="input"  /> 

            // 4. 父组件提供给子组件 toF,toF中传的是方法,当子组件调用,就可以把子组件里的值sonMes传给父组件,进而执行setSonMes(sonMes)
            <button className="searchbtn" onClick={ ()=> this.props.toF(sonMes) }  >to father</button>
        </div>
    }
}



/* 父组件 */
export default function Father(){
    const [ sonMes , setSonMes ] = React.useState('') // setSonMes 

    const sonInstance = React.useRef(null) /* 3. useRef 产生的 ref 对象。用来获取子组件实例 */

    const [ fatherMes , setFatherMes ] = React.useState('') // 1.把父组件输入框里的值存起来

    const toSon =()=> sonInstance.current.fatherSay(fatherMes) /* 2. 调用子组件实例方法,改变子组件state */

    return <div className="box" >
        <div className="title" >父组件</div>

        // 5. 在这里应用子组件传来的值
        <p>子组件对我说:{ sonMes }</p>

        // 1. 把父组件输入框里的值存起来,
        <div className="label" >对子组件说</div> <input onChange={ (e) => setFatherMes(e.target.value) }  className="input"  /> 

        // 2. 点击发送给儿子时调用儿子暴露出的方法fatherSay来传递刚才存的值
        <button className="searchbtn"  onClick={toSon}  >to son</button>

        // 3. 父组件用ref标记子组件
        // 4. 父组件提供给子组件 toF,toF中传递的是方法
        <Son ref={sonInstance} toF={setSonMes} />
    </div>
}

【流程分析】:

  • 1 子组件暴露方法 fatherSay 供父组件使用,父组件通过调用方法可以设置子组件展示内容。
  • 2 父组件提供给子组件 toFather,子组件调用,改变父组件展示内容,实现父 <-> 子 双向通信。

【效果】

ref5.gif

 场景二:函数组件 forwardRef + useImperativeHandle

对于函数组件,本身是没有实例的,但是 React Hooks 提供了,useImperativeHandle 一方面第一个参数接受父组件传递的 ref 对象,另一方面第二个参数是一个函数,函数返回值,作为 ref 对象获取的内容。一起看一下 useImperativeHandle 的基本使用。

useImperativeHandle 接受三个参数:

  • 第一个参数 ref : 接受 forWardRef 传递过来的 ref 。
  • 第二个参数 createHandle :处理函数,返回值作为暴露给父组件的 ref 对象。
  • 第三个参数 deps :依赖项 deps,依赖项更改形成新的 ref 对象。

forwardRef + useImperativeHandle 可以完全让函数组件也能流畅的使用 Ref 通信。其原理图如下所示:

// 子组件
function Son (props,ref) {
    const inputRef = useRef(null)
    const [ inputValue , setInputValue ] = useState('')
    useImperativeHandle(ref,()=>{
       const handleRefs = {
           onFocus(){              /* 声明方法用于聚焦input框 */
              inputRef.current.focus()
           },
           onChangeValue(value){   /* 声明方法用于改变input的值 */
               setInputValue(value)
           }
       }
       return handleRefs
    },[])
    return <div>
        <input placeholder="请输入内容"  ref={inputRef}  value={inputValue} />
    </div>
}

const ForwarSon = forwardRef(Son)
// 父组件
class Index extends React.Component{
    cur = null
    handerClick(){
       const { onFocus , onChangeValue } =this.cur
       onFocus() // 让子组件的输入框获取焦点
       onChangeValue('let us learn React!') // 让子组件input  
    }
    render(){
        return <div style={{ marginTop:'50px' }} >
            <ForwarSon ref={cur => (this.cur = cur)} />
            <button onClick={this.handerClick.bind(this)} >操控子组件</button>
        </div>
    }
}

效果图

流程分析:

  • 父组件用 ref 标记子组件,由于子组件 Son 是函数组件没有实例,所以用 forwardRef 转发 ref。
  • 子组件 Son 用 useImperativeHandle 接收父组件 ref,将让 input 聚焦的方法 onFocus 和 改变 input 输入框的值的方法 onChangeValue 传递给 ref 。
  • 父组件可以通过调用 ref 下的 onFocus 和 onChangeValue 控制子组件中 input 赋值和聚焦。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React中的ref是一个用来获取组件或DOM元素的返回值的属性。在React的生命周期函数中,你可以使用ref来强制组件重新渲染。 使用ref主要用来访问DOM元素,例如输入框、按钮等。使用ref可以实现获取输入框中的文本、获取按钮的值等操作。 ref有两种使用方式:string refs和function refs。 string refs是React较早时引入的一种使用方式,现在已经不再推荐使用。使用string refs需要给元素设置ref属性,值为字符串,然后将ref值赋值给一个成员变量。实例如下: ``` class MyComponent extends React.Component { componentDidMount() { console.log(this.inputRef.value); } render() { return( <input type="text" ref={(input) => this.inputRef = input} /> ) } } ``` function refs是现在推荐使用的一种方式,可以更好的控制和管理组件的引用。使用function refs需要将一个函数作为ref的值,这个函数会在组件挂载或卸载时被执行。实例如下: ``` class MyComponent extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); } componentDidMount() { console.log(this.inputRef.current.value); } render() { return ( <input type="text" ref={this.inputRef} /> ) } } ``` 总结而言,ref是一个非常好用的工具,能够让开发人员更加方便的操作DOM元素,并且更好的控制和管理组件的引用。但是,需要注意的是,过度使用ref会使代码变得混乱难以维护,建议谨慎使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值