React ref用法

124 阅读2分钟

blog.csdn.net/weixin_4360…

React 中通过ref实现组件间传值

React 中的 forwardRef 的理解及用法

语法

React.forwardRef(render);

上面的代码中,forwardRef 函数接收一个名为 render 的函数(也可以将 render 方法理解成一个函数组件),返回值是 react 组件;

const render = (props, ref) => {
	return <></>;
}

上面的代码中,render 函数接收 2 个参数,第一个参数为 props(父组件传递的参数对象),第二个参数为 ref (React.createRef());

综上所述,就组合成了我们就常见到的写法:

const Button = React.forwardRef((props, ref) {
	return <></>;
});

一个完整的简单例子

import React from "react";

const App = (props) => {
  const btnRef = React.createRef();			// 1
  React.useEffect(() => {
    console.log(btnRef);					// 5
  }, [btnRef]);
  return (
    <Button ref={btnRef}>一个按钮</Button>	// 2
  );
}

const Button = React.forwardRef((props, ref) => {	// 3
  return (
    <button
      ref={ref}								// 4
    >
      {props.children}
    </button>
  );
});

export default App;

上面代码中,实现了在 App 组件中,返回了一个 Button 组件;以下是详细步骤:

  1. 创建了一个 ref;
  2. 将第一步创建的 ref 挂载到 Button 组件上;
  3. 将第二步传入 Button 组件的 ref 作为参数传递进来;
  4. 将 ref 挂载到 button DOM 节点上;
  5. 画面渲染完成后,控制台打印 btnRef;

总结

值得注意的是:

  1. 当 ref 属性用于 HTML 元素时,接收底层 DOM 元素作为其 current 属性;
  2. 当 ref 属性用于自定义 class 组件时,ref 接收组件的挂载实例作为其 current 属性;
  3. 不能在函数组件上使用 ref 属性,因为他们没有实例;

此时再来理解一下官方的定义:

Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。

React hooks 中ref、useRef和forwardRef 的用法(父子组件通讯)

父调子组件 DOM 聚焦

 
import React, { useRef, forwardRef } from 'react';
import { Card, Button } from 'antd';
 
const Child = forwardRef((props,cRef) => {
  // const inputRef = useRef()
  const onClick = () => {
    cRef.current.focus()
  }
  
  return (
    <div style={{border: '1px solid red'}}>
      <input type='text' ref={cRef}></input>
      <Button onClick={onClick}>子组件DOM聚焦</Button>
    </div>
  )
})
 
const App: React.FC = () => {
  const cRef:any = useRef() // 或 const cRef:any = createRef()
  const onClick = () => {
    if(cRef.current){
      cRef.current.focus()
    }
  }
  
  return (
    <Card>
      <Child ref={cRef}></Child>
      <br/>
      <Button onClick={onClick}>父调子组件DOM 聚焦</Button>
    </Card>
  )
}
export default App

父调子组件 Func 聚焦

 
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
import { Card, Button } from 'antd';
 
const Child = forwardRef((props,cRef) => {
  const inputRef = useRef()
  const onClick = () => {
    inputRef.current.focus()
  }
  useImperativeHandle(cRef, () => ({
    chidFunc: () => {
      console.log('这是子组件函数')
      inputRef.current.focus();
    }
  }));
  
  return (
    <div style={{border: '1px solid red'}}>
      <input type='text' ref={inputRef}></input>
      <Button onClick={onClick}>子组件DOM聚焦</Button>
    </div>
  )
})
 
const App: React.FC = () => {
  const cRef:any = useRef() // 或 const cRef:any = createRef()
  const onClickChildren = () => {
    if(cRef.current){
      cRef.current.chidFunc()
    }
  }
  
  return (
    <Card>
      <Child ref={cRef}></Child>
      <br/>
      <Button onClick={onClickChildren}>父调子组件Func 聚焦</Button>
    </Card>
  )
}
export default App

注:编写中发现同时操作DOM、Func有冲突,会报错,暂未解决。

官方useImperativeHandle示例:

import React, { useRef, useImperativeHandle } from 'react';
import ReactDOM from 'react-dom';
 
const FancyInput = React.forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
 
  return <input ref={inputRef} type="text" />
});
 
const App = props => {
  const fancyInputRef = useRef();
 
  return (
    <div>
      <FancyInput ref={fancyInputRef} />
      <button
        onClick={() => fancyInputRef.current.focus()}
      >父组件调用子组件的 focus</button>
    </div>
  )
}
 
ReactDOM.render(<App />, root);

其他

import React, { forwardRef, useImperativeHandle, useEffect, useRef } from 'react'
 
const TestRef = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    open() {
      console.log("open")
    }
  }))
})
 
function App () {
  const ref = useRef()
  useEffect(() => {
    ref.current.open()
  },[])
  
  return(
    <>
      <div>石小阳</div>
      <TestRef ref={ref}></TestRef>
    </>
  )
}
export default App