如何在 React 中使用 forwardRef()?

IT技术 javascript reactjs ref
2021-04-28 22:02:46

我目前在我的 React 应用程序中收到以下错误:

不能给函数组件提供 refs。尝试访问此引用将失败。你的意思是使用 React.forwardRef() 吗?

如何使用 forwardRef() 解决此问题?

我的代码如下:

const Services: FunctionComponent = (): ReactElement => {
  const servicesRef = useRef(null);

  return (
    <Layout>
      <ServicesList ref={servicesRef} />
    </Layout>
  );
};
export default Services;



const ServicesList: React.FunctionComponent = ({ children }: Props) => {
  return (
    <section className="my-24 md:my-32">
      {children && children}
    </section>
  );
};

export default ServicesList;
2个回答

forwardRefAPI,(加上useImperativeHandle挂机),您可以自定义如何与您的裁判都放在你的自定义组件里。此外,forwardRef 是将 ref 传递给自定义函数组件的唯一方法。

首先,重要的是要了解 refs 在类组件、函数组件和常规 DOM 元素上的工作方式不同。

文档

ref 的值因节点类型而异:

  1. 当 ref 属性用于 HTML 元素时,在构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其当前属性。
  2. 当 ref 属性用于自定义类组件时,ref 对象接收组件的已安装实例作为其当前实例。
  3. 您不能在函数组件上使用 ref 属性,因为它们没有实例。

以下是在不同元素类型上使用 refs 的方法的示例:

1. 对 DOM 元素的引用为您提供了对 DOM 节点本身的引用:
function AutoFocusInput() {
  const inputRef = useRef(null);
  // This effect runs only once after the component mounts (like componentDidMount)
  useEffect(() => {
    // refs on regular DOM elements (e.g. the "input" tag) have access to the DOM node
    inputRef.current.focus();
  }, []);
  return <input ref={inputRef} />
}
2. 类组件上的引用使我们可以访问实例,以及它的所有方法和字段:
class Child extends Component {
  state = {color: "red"}
  toggleColor = () => this.setState({color: this.state.color === "red" ? "blue" : "red"})
  render() {
    return <div style={{backgroundColor: this.state.color}}>yo</div>
  }
}

class Parent extends Component {
  childRef = createRef();
  handleButtonClicked = () => {
    // refs on class components: hold the class component instance, 
    // allowing us to call its methods!
    this.childRef.current.toggleColor();
  }
  render() {
    return (
      <div>
        <button onClick={this.handleButtonClicked}>toggle color!</button>
        <Child ref={childRef} />
      </div>
    );
  }
}
3. 现在,终于可以回答你的问题了。Refs 不能传递给函数组件,因为它们没有实例!

将 ref 传递给函数组件的唯一方法是使用 forwardRef。使用 forwardRef 时,您可以简单地将 ref 传递给 DOM 元素,以便父元素可以像示例 1 中那样访问它,或者您可以使用 useImperativeHandle 钩子创建具有字段和方法的对象,这类似于示例 2

3.1 简单地将 ref 传递给 DOM 元素:
// Only when using forwardRef, the function component receives two arguments, 
// props and ref (Normally the component only gets the props argument).
const RedInput = forwardRef((props, ref) => {
  // passing the ref to a DOM element, 
  // so that the parent has a reference to the DOM node
  return <input style={{color: "red"}} {...props} ref={ref} />
});

function AutoFocusInput() {
  const inputRef = useRef(null);
  // This effect runs only once after the component mounts (like componentDidMount)
  useEffect(() => {
    // ref on function component is forwarded to a regular DOM element, 
    // so now the parent has access to the DOM node including its focus method.
    // Note that the ref usage is the same as a regular 
    // DOM element, like in example 1!
    inputRef.current.focus();
  }, []);
  return <RedInput ref={inputRef} />
}
3.2 将父引用附加到自定义对象:

要将函数或字段附加到 ref,就像您可以使用类组件的实例一样,您需要使用 `useImperativeHandle` 钩子:

const Child = forwardRef((props, ref) => {
  const [color, setColor] = useState("red");
  // To customize the value that the parent will get in their ref.current: 
  // pass the ref object to useImperativeHandle as the first argument. 
  // Then, whatever will be returned from the callback in the second argument, 
  // will be the value of ref.current. 
  // Here I return an object with the toggleColor method on it, for the parent to use:
  useImperativeHandle(ref, () => ({
    toggleColor: () => setColor(prevColor => prevColor === "red" ? "blue" : "red")
  }));
  return <div style={{backgroundColor: color}}>yo</div>;
});


class Parent extends Component {
  childRef = createRef();
  handleButtonClicked = () => {
    // Ref passed to a function component wrapped in forwardRef.
    // Note that nothing has changed for this Parent component
    // compared with the class component in example 2!
    this.childRef.current.toggleColor();
  }
  render() {
    return (
      <div>
        <button onClick={this.handleButtonClicked}>toggle color!</button>
        <Child ref={childRef} />
      </div>
    );
  }
}

React 文档解释得很好..

https://reactjs.org/docs/forwarding-refs.html

用 React.forwardRef 包裹你的组件并将 ref 设置为预期的 DOM 元素

const FancyButton = React.forwardRef((props,ref)=>( <buttonref={ref} className="FancyButton"> {props.children} ) );