JSX 中的 ref与useRef --- react 篇

120 阅读5分钟

我们在编写react的时候,很多情况下,都是操作状态、方法,然后通过hook或者render 让视图重新更新,进而达到预期展示效果。我们将这个叫做 受控组件。但是在一些特定情况下,比如父组件操控子组件内部属性方法、组件获取某个DOM,进而操作DOM,来实现需求和效果。我们叫这个为 非受控组件。当我们获取DOM 或者组件实例的时候,会用到 REF 这个属性。

获取DOM元素

1. 给元素直接设置字符串方式

  • 给需要获取的元素设置 ref="strName" js中通过 this.refs.strName 来获取相应的的DOM元素

  • 这种方式只适用于 类组件中获取 DOM元素。

  • 该方式 慢慢会被取缔,所以不建议使用

     class RefSty extend React.component {
         render() {
            return(
                <div ref="refStr">ref字符串使用方式</div>
            )
        }
        
        componentDidMount() {
            log(this.refs.refStr);
        }
     }
    

2. 给元素ref属性 设置 function

  • 直接在元素上绑定 ref={domPar=>this.refFn=domPar},

    • 如果是类组件,在js 中,使用 this.refs.refFn 来获取DOM元素 【domPar是函数的形参,存储当前的DOM值】
    • 如果是函数组件,在js中,直接获取对应的变量值就好。注意函数组件没有this一说,所以 {domPar=>refFn=domPar} 这样来处理
    /// 类组件
    class RefSty extend React.component {
         render() {
            return(
                <div ref={domPar=>this.refFn=domPar}>ref函数式使用方式</div>
            )
        }
        
        componentDidMount() {
            log(this.refFn);
        }
     }
     
     /// 函数组件
     export default function RefStateFn(props) {
        let refFn;
        useEffect(()=>{
            console.log(refFn);
        })
        return (
            <div className='demo'>
                <p ref={x=>refFn=x}>ref函数式使用方式</p>
            </div>
        )
    }
    
  • 函数组件使用 ref的函数方法,由于每次更新,函数组件都会重新渲染,然后ref会重新执行,创建新的上下文、变量这些内容,所以在函数组件中,并不推荐使用

3. 使用creatRef方式获取DOM

  • 给状态绑定 curRef=React.createEef()方法,默认返回 {current: null}

    • 类组件中:在DOM结构上直接使用ref={this.curRef}, js中可以通过 this.curRef.current 来获取到DOM
    • 静态组件中:DOM结构使用 ref={curRef} ,js中使用 curRef.current 来操作。
    /// 类组件
    class RefSty extend React.component {
         curRef=React.createEef();
         render() {
            return(
                <div ref={curRef}>ref的createRef使用方式</div>
            )
        }
        
        componentDidMount() {
            log(this.curRef.current);
        }
     }
     
     /// 函数组件
     export default function RefStateFn(props) {
        let curRef=React.createRef();
        useEffect(()=>{
            console.log(curRef.current);
        },[])
        return (
            <div className='demo'>
                <p ref={curRef}>ref函数式使用方式</p>
            </div>
        )
    }
    

4. 使用 useRef 获取 DOM

  • useRef 属于 hook 函数,所以只能在函数组件中使用。禁止在类组件中使用

    export default function UseStateFn(props) {
        let box = useRef(null);
        useEffect(()=>{
            console.log(box.current) 
        },[]);
    
        return (
            <div className='demo' ref={box}>
                ref的hook函数useRef使用方式
            </div>
        )
    }
    
    • 第一步: 创建 useRef 对象 let box = useRef(null) => {current: null}
    • 第二步: 给DOM元素绑定 ref ref={box}
    • 第三步: 在创建出DOM后,使用 box.current 直接获取

5. useRef 和 createRef 的比较

  • useRefReact.createRef, 两者在 初次创建的时候,都可以获取到DOM值。
  • useRef 属于hook函数,所以只能在函数组件中使用
  • useRef 在更新的时候,不会再重新创建 REF 对象,直接使用的是 初次创建的 DOM对象
  • React.createRef 是react提供的一个方法,可以在类组件和 函数组件中使用
  • React.createRef 在函数中使用,更新时候,函数会重新执行,然后通过 createRef 创建的Ref对象,会重新创建的。所以在 函数组件中,不建议使用 createRef 方法。

ref 绑定 类组件

  • 我们通过 REF 获取到类组件信息,都是 类组件的实例信息,我们可以通过实例信息去获取其内部的 状态、方法等。

1. 使用基本方式 绑定

  • 在父组件插入子组件[类]时候,可以直接在 子组件中使用。
    class Child extend React.Component{
        state = {x: 123}
        render(){
            return (
                <div> 子组件 </div>
            )
        }
    }
    
    class Parent extend React.Component{
        componentDidMount() {
            log(this.childComp)
        }
        render() {
            return <>
                <Child ref={refPar => this.childComp = refPar} />
            </>
        }
    }
    

2. hook 绑定

  • hook函数只能用于函数组件中,所以当父组件是函数组件、子组件属于类组件时候可以使用
    
    /// 父组件  函数组件
    export default function UseStateFn(props) {
        let box = useRef(null);
        useEffect(() => {
            console.log(box, 'box')
        }, []);
        return (
            <>
                <Child ref={box} />
            </>
        )
    }
    
    /// 子组件  类组件
    class Child extends React.Component {
        state = {
            x: 123,
        }
        render() {
            return (
                <div>
                    <p>childX: {this.state.x}</p>
                </div>
            )
        }
    }
    

ref绑定子函数组件

1. ref + React.forwardRef();

  • 给函数组件直接设置 ref,是会报错的。

    • Function components cannot be given refs.Attempts to access thisref will fail.
    • 我们需要通过 React.forWardRef 来实现转发,获取到组件内部的某个元素
    const Child =React.forwardRef(function Child(_props, ref){
        return <>
            <button ref={ref}>按钮</button>
        </>
    })
    
    class Parent extends React.Component {
        render() {
            return (
                <div>
                    <Child ref={params => this.child = params}/>
                </div>
            )
        }
        
        componentDidMount() {
            log(this.child)
        }
    }
    
    function Parent1() {
        let child = React.createRef();
        useEffect(()=>{
            log(child.current)
        })
        return <>
            <Child ref={child} />
        </>
    }
    

    2. 使用hook 获取函数组件

    • 需要借助 React.forward, 获取到的是 函数组件某个DOM元素的信息
      /// parent
      export default function UseStateFn(props) {
          let box = useRef(null);
          useEffect(() => {
              console.log(box, 'box')
          }, []);
          return (
              <div className='demo'>
                  <ChildFn ref={box} />
              </div>
          )
      }
      
      /// child
      const ChildFn = React.forwardRef(function Child(props, refs) {
          console.log(refs, 'childFn');
          return (
              <div>
                  <p ref={refs}>这是一个函数的子组件</p>
              </div>
          )
      })
      

    3. 想获取函数组件内部的状态、方法

    • 需要 配合 useImperativeHandle 来一起使用。

    • useImperativeHandle(ref,()=>{ return{} })

      /// parent 
      function Parent() {
          let child = useRef(null);
          useEffect(()=>{
              log(child.current)
          })
          return (
              <Child ref={child}/>
          )
      }
      
      /// child
      const Child = React.forwardRef(function Child(props, ref){
          let [state, setState] = useState(10);
          const handle = ()=> {};
          useImperativeHandle(ref,()=>{
              return {
                  state, 
                  handle
              }
          })
          return <p> 子组件信息{state} </p>
      })
      
  • 目前想到的ref相关内容是这么多。总结:

    • ref 可以是字符串,函数,变量值。
    • ref 可以存储DOM元素和组件的实例
    • 在处理函数组件的时候,ref 需要和 React.forwardRef 一起使用。
    • 在类组件中,我们获取的是类组件的实例,可以直接拿到对应的状态、方法等所有信息。
    • 在函数组件中,正常情况下,通过参数传递进入,然后通过ref绑定,拿到某个元素
    • 为了让函数组件也可以获取到 属性方法,hook函数中提供了一个 React.useImperativeHandle 方法,第二个参数的返回值,存储的就是可以获取到的 状态、方法。