该forwardRef
API,(加上useImperativeHandle
挂机),您可以自定义如何与您的裁判都放在你的自定义组件里。此外,forwardRef 是将 ref 传递给自定义函数组件的唯一方法。
首先,重要的是要了解 refs 在类组件、函数组件和常规 DOM 元素上的工作方式不同。
从文档:
ref 的值因节点类型而异:
- 当 ref 属性用于 HTML 元素时,在构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其当前属性。
- 当 ref 属性用于自定义类组件时,ref 对象接收组件的已安装实例作为其当前实例。
- 您不能在函数组件上使用 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>
);
}
}