zoukankan      html  css  js  c++  java
  • react进阶第五讲——ref

    ref对象的创建

    1. 类组件 React.createRef
    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.createRef 的底层逻辑:

    export function createRef() {
      const refObject = {
        current: null,
      }
      return refObject;
    }
    

    createRef 创建了一个对象,对象上的 current 属性,用于保存通过 ref 获取的 DOM 元素,组件实例等。

    1. 函数组件 useRef
    export default function Index(){
        const currentDom = React.useRef(null)
        React.useEffect(()=>{
            console.log( currentDom.current ) // div
        },[])
        return  <div ref={ currentDom } >ref对象模式获取元素或组件</div>
    }
    

    问:在函数组件中为什么不能用 createRef ?

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

    React.useRef的内部实现:

    hooks 和函数组件对应的 fiber 对象建立起关联,将 useRef 产生的 ref 对象挂到函数组件对应的 fiber 上,函数组件每次执行,只要组件不被销毁,函数组件对应的 fiber 对象一直存在,所以 ref 等信息就会被保存下来。


    具体原理后面的hooks部分再详细讲解


    类组件获取 Ref 三种方式

    1. 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>
    }
    

    打印:
    image

    1. 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>
    }
    

    打印:
    image

    1. 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>
    }
    

    打印:
    image

    ref的高级用法

    1. forwardRef 解决 ref 跨层级捕获和传递的问题。
    // 表单组件
    class Form extends React.Component{
        render(){
           return <div>{...}</div>
        }
    }
    // index 组件
    class Index extends React.Component{ 
        componentDidMount(){
            const { forwardRef } = this.props
            forwardRef.current={
                form:this.form,      // 给form组件实例 ,绑定给 ref form属性 
                index:this,          // 给index组件实例 ,绑定给 ref index属性 
                button:this.button,  // 给button dom 元素,绑定给 ref button属性 
            }
        }
        form = null
        button = null
        render(){
            return <div   > 
              <button ref={(button)=> this.button = button }  >点击</button>
              <Form  ref={(form) => this.form = form }  />  
          </div>
        }
    }
    const ForwardRefIndex = React.forwardRef(( props,ref )=><Index  {...props} forwardRef={ref}  />)
    // home 组件
    export default function Home(){
        const ref = useRef(null)
         useEffect(()=>{
             console.log(ref.current)
         },[])
        return <ForwardRefIndex ref={ref} />
    }
    
    1. ref实现组件通信

      1)类组件ref
    /* 子组件 */
    class Son extends React.PureComponent{
        state={
           fatherMes:'',
           sonMes:''
        }
        fatherSay=(fatherMes)=> this.setState({ fatherMes  }) /* 提供给父组件的API */
        render(){
            const { fatherMes, sonMes } = this.state
            return <div className="sonbox" >
                <div className="title" >子组件</div>
                <p>父组件对我说:{ fatherMes }</p>
                <div className="label" >对父组件说</div> <input  onChange={(e)=>this.setState({ sonMes:e.target.value })}   className="input"  /> 
                <button className="searchbtn" onClick={ ()=> this.props.toFather(sonMes) }  >to father</button>
            </div>
        }
    }
    /* 父组件 */
    export default function Father(){
        const [ sonMes , setSonMes ] = React.useState('') 
        const sonInstance = React.useRef(null) /* 用来获取子组件实例 */
        const [ fatherMes , setFatherMes ] = React.useState('')
        const toSon =()=> sonInstance.current.fatherSay(fatherMes) /* 调用子组件实例方法,改变子组件state */
        return <div className="box" >
            <div className="title" >父组件</div>
            <p>子组件对我说:{ sonMes }</p>
            <div className="label" >对子组件说</div> <input onChange={ (e) => setFatherMes(e.target.value) }  className="input"  /> 
            <button className="searchbtn"  onClick={toSon}  >to son</button>
            <Son ref={sonInstance} toFather={setSonMes} />
        </div>
    }
    
    1. 函数组件 forwardRef + useImperativeHandle

      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>
        }
    }。
    
    1. 函数组件缓存数据

      如果视图的更新不想依赖于改变的数据,可以将改变的数据存放在ref对象中。因为,只要组件没有销毁,ref 对象就一直存在。
  • 相关阅读:
    Head first java chapter 8 接口与抽象类
    Head first java chapter 4 对象的行为
    Head first java chapter 3认识变量
    Head first java chapter 2 拜访对象村
    Head first java chapter 1
    Invalid left-hand side in assignment
    swtich多个case使用同一操作
    CSS绘制小三角
    超出文字出现省略号不换行
    css代码实现列表等宽
  • 原文地址:https://www.cnblogs.com/renzhiwei2017/p/15648484.html
Copyright © 2011-2022 走看看