React Ref

429 阅读1分钟

Ref的高阶用法

1.forwardRef 转发 Ref

1.跨层级获取

场景 : 想要在GrandFather组件通过标记ref,来获取孙组件Son的组件实例

import * as React from 'react';

interface ISonProps {
  grandRef:React.ForwardedRef<HTMLElement>
}

//孙组件
function Son(props:ISonProps){
  const {grandRef} = props
  return (
    <div>
      <div>I am Son</div>
      <span ref={grandRef}>这个是想要获取的元素</span>
    </div>
  )
}
//父组件
interface IFatherProps {
  grandRef:React.ForwardedRef<HTMLElement>
}
class Father extends React.Component<IFatherProps>{
  constructor(props:IFatherProps){
    super(props)
  }
  render(): React.ReactNode {
    return(
      <div>
        <Son grandRef={this.props.grandRef}></Son>
      </div>
    )
  }
}

const NewFather = React.forwardRef((props,ref:React.ForwardedRef<HTMLElement>)=><Father grandRef={ref} {...props} />)

interface IGrandFather {}
class GrandFather extends React.Component<IGrandFather>{
  constructor(props:IGrandFather){
    super(props)
  }
  node:HTMLElement|null = null
  componentDidMount(){
    console.log(this.node)  //打印想要获取的元素
  }
  render(): React.ReactNode {
    return(
      <div>
        <NewFather ref={(node)=>this.node=node} />
      </div>
    )
  }
}
export default GrandFather

2.合并转发Ref

场景 : 通过Home绑定ref,来获取子组件Index的实例Index,dom元素的Button,以及孙组件Form的实例

import * as React from 'react';

class Form extends React.Component{
  render(): React.ReactNode {
    return(
      <form>
        <label htmlFor="username">用户名</label>
        <input type="text" id='username' />
      </form>
    )
  }
}

interface IIndexProps {
  forwardRef:React.ForwardedRef<HTMLElement>
}
class Index extends React.Component<IIndexProps>{
  componentDidMount(){
    const {forwardRef} = this.props
    forwardRef.current = {
      form:this.form,
      index:this,
      button:this.button
    }
  }
  form:Form|null = null
  button:HTMLElement|null = null
  render(){
    return(
      <div>
        <button ref={(button)=>this.button=button}>点击</button>
        <Form ref={(form)=>this.form=form}></Form>
      </div>
    )
  }
}

const ForwardRefIndex = React.forwardRef((props,ref:React.ForwardedRef<HTMLElement>)=><Index {...props} forwardRef={ref}/>)

export default function Home(){
  const ref = React.useRef(null)
  React.useEffect(()=>{
    console.log(ref.current); 
  },[])
  return <ForwardRefIndex ref={ref}/>
}

3.高阶组件转发

import * as React from 'react';

function HOC(Component){
  interface IWrapProps{
    forwardedRef:React.ForwardedRef<HTMLElement>
  }
  class Wrap extends React.Component<IWrapProps>{
    render(): React.ReactNode {
      const {forwardedRef,...otherProps} = this.props
      return (
        <Component ref={forwardedRef} {...otherProps} />
      )
    }
  }
  return React.forwardRef((props,ref:React.ForwardedRef<HTMLElement>)=><Wrap forwardedRef={ref} {...props} />)
}

class Index extends React.Component{
  render(): React.ReactNode {
    return (
      <div>Hello World</div>
    )
  }
}

const HocIndex = HOC(Index)

export default function Pages(){
  const node = React.useRef(null)
  React.useEffect(()=>{
    console.log(node.current)
  },[])
  return (
    <div><HocIndex ref={node} /></div>
  )
}

2.Ref实现组件通信

1.类组件 ref

import * as React from 'react';
interface ISonProps{
  toFather:React.Dispatch<React.SetStateAction<string>>
}
class Son extends React.PureComponent<ISonProps>{
  state={
    fatherMsg:"",
    sonMsg:""
  }
  fatherSay = (fatherMsg:string)=>this.setState({fatherMsg})
  render(): React.ReactNode {
    const {fatherMsg,sonMsg} = this.state
    return(
      <div className='son-box'>
        <div className='title'>子组件</div>
        <p>父组件对我说:{fatherMsg}</p>
        <div className='label'>对父组件说</div>
        <input type="text" onChange={e=>this.setState({sonMsg:e.target.value})} className="input"/>
        <button className='search-btn' onClick={()=>this.props.toFather(sonMsg)}>to Father</button>
      </div>
    )
  }
}

export default function Father(){
  const [sonMsg,setSonMsg] = React.useState('')
  const sonInstance = React.useRef(null)
  const [fatherMsg,setFatherMsg] = React.useState('')
  const toSon = ()=>sonInstance.current.fatherSay(fatherMsg)
  return(
    <div className='box'>
      <div className="title">父组件</div>
      <p>子组件对我说:{sonMsg}</p>
      <div className="label">对子组件说</div>
      <input type="text" onChange={e=>setFatherMsg(e.target.value)} className="input" />
      <button className='search-btn' onClick={toSon}>to Son</button>
      <Son ref={sonInstance} toFather={setSonMsg} />
    </div>
  )
}

2.forwardRef + useImperativeHandle

import * as React from 'react';

function Son(props,ref){
  const inputRef = React.useRef(null)
  const [inputValue,setInputValue] = React.useState('')
  React.useImperativeHandle(ref,()=>{
    const handleRefs = {
      onFocus(){
        inputRef.current.focus()
      },
      onChangeValue(value){
        setInputValue(value)
      }
    }
    return handleRefs
  },[])
  return (
    <div>
      <input type="text" placeholder='请输入内容' ref={inputRef} value={inputValue} />
    </div>
  )
}

const ForwardSon = React.forwardRef(Son)

export default function Index(){
  let curSon = null
  let handerClick=()=>{
    const {onFocus,onChangeValue} = curSon
    onFocus()
    onChangeValue("let us learn React")
  }
  return (
    <div>
        <ForwardSon ref={cur=>curSon=cur} />
        <button onClick={handerClick}>操控子组件</button>
    </div>
  )
}

函数组件缓存数据