const Comp1 = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
print: () => {
console.log('comp1')
}
}), []);
return <div>comp1</div>
});
const Comp2 = () => <div>comp2</div>;
const App = () => {
const ref1 = useRef(null);
const ref2 = useRef(null);
useEffect(() => {
console.log(ref1); // prints ref1 with the expected current
console.log(ref2); // prints ref2 with current: null
})
return <div><Comp1 ref={ref1}/><Comp2 ref={ref2}/></div>
}
答案 0 :(得分:2)
反应文档说:
您不能在功能组件上使用ref属性,因为它们没有实例。 (more)
这意味着您不能将引用绑定到功能组件。这就是为什么您的ref2.current
是null
的原因。如果要将引用绑定到组件,则需要使用类组件。您的ref1
不是对Comp1
组件的引用。它实际上包含您在useImperativeHandle
挂钩中传递的对象。即它包含下一个对象:
{
print: () => {
console.log('comp1')
}
}
如果要将引用与组件呈现的某些HTML元素或类组件绑定,则必须对功能组件使用forwardRef
。或者,您可以使用useImperativeHandle
钩子将ref与某些对象绑定。
更新
useImperativeHandle
的使用与向类组件添加方法相同:
class Comp1 extends React.Component {
print() {
console.log('comp1');
}
render() {
return (<div>comp1</div>)
}
}
与
相同const Comp1 = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
print: () => {
console.log('comp1')
}
}), []);
return <div>comp1</div>
});
您在评论中问:
因此,在转到钩子之后(仍然避免使用类),使用ref的唯一方法是使用
useImperativeHandle
(并实际上使用“假” ref)?这是一个好习惯吗?
答案:使用useImperativeHandle
与通过类组件中的引用调用子组件方法的不良做法相同。 React doc表示您应避免通过引用调用子组件方法,应避免使用useImperativeHandle
。另外,您需要避免使用ref而不使用它们的情况。
答案 1 :(得分:1)
我将尝试回答问题2
useImperativeHandle描述了引用调用语法将与 某些公开给父对象的实例值的自定义方法。
forwardRef有助于通过一个高阶组件转发参考以引用内部DOM节点。
当您尝试聚焦时:ref1.current.focus(),子输入将被聚焦,而不是包裹子输入的高阶组件。
使用useImperativeHandle的setValue方法查看与答案有关的简单示例,并在ChildInput中转发输入: https://codesandbox.io/s/react-hook-useimperativehandle-huktt
答案 2 :(得分:0)
1。 Comp1和Comp2引用之间有什么区别?
Comp1
使用React.forwardRef
,将能够从父级接收给定的ref
。
Comp2
无法正常工作并触发以下错误:
警告:不能为功能组件提供引用。尝试访问此引用将失败。您是要使用
React.forwardRef()
吗?
<Child ref={ref1}/>
就像来自父组件的请求:“嘿Child
,请将您的组件引用或类似内容传递到给定的可变存储盒(ref
)中,以便我可以调用一个方法或直接操作包含的DOM节点。”
// React internally calls `new ClassComp()`, instance can be stored and passed in a ref
<ClassComp ref={compRef} />
// React just *calls* the function for re-renders, there is no plain function "instance"
<FunctionComp ref={compRef} />
React.forwardRef
解决了上述限制。用
const FunctionComp = React.forwardRef((props, ref) => <div ref={ref}>Hello FnComp</div>
,尽管没有实例,FunctionComp
仍可以传递代表ref
给定的Parent
的东西(如div
DOM节点)。 ClassComp
会在此处传递其实例。
2。为什么必须将
forwardRef
与useImperativeHandle
一起使用才能真正获得对Comp1
的引用?
您不必。 useImperativeHandle
是提供更多自定义命令式调用API的扩展。以下三种选择是等效的:
forwardRef
:
const App = () => {
const compRef = React.useRef();
return (
<div>
<Comp ref={compRef} />
<button
onClick={() => {
compRef.current.focus();
}}
>
Focus input
</button>
</div>
);
}
const Comp = React.forwardRef((props, ref) => {
const inputRef = React.useRef();
return <input ref={ref} />;
});
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
仅
useImperativeHandle
,带有自定义参考道具(有关更多信息,请查看this answer):
const App = () => {
const compRef = React.useRef();
return (
<div>
<Comp customRef={compRef} />
<button
onClick={() => {
compRef.current.focus();
}}
>
Focus input
</button>
</div>
);
}
const Comp = ({ customRef }) => {
const inputRef = React.useRef();
React.useImperativeHandle(customRef, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
};
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
useImperativeHandle
+ forwardRef
:
const App = () => {
const compRef = React.useRef();
return (
<div>
<Comp ref={compRef} />
<button
onClick={() => {
compRef.current.focus();
}}
>
Focus input
</button>
</div>
);
}
const Comp = React.forwardRef((props, ref) => {
const inputRef = React.useRef();
React.useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
});
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>