一、ref简介
- React中的
ref
属性可以帮助我们获取子组件的实例或者Dom对象,进而对子组件进行修改,是一个很方便的特性。 - 在传统类组件中,我们通过使用
React.createRef()
创建的,并通过ref
属性附加到React 元素
来使用。而随着hooks的越来越广泛的使用,我们有必要了解一下在函数式组件中,如何使用Ref?想要在函数式组件中使用Ref,我们必须先了解两个Api:useRef
和forwardRef
。
二、useRef
const refContainer = useRef(initialValue);
useRef返回一个可变的ref
对象,其.current
属性被初始化为传入的参数(initialValue)
。返回的ref对象在整个生命周期内保持不变。
例子
const App = ()=> {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current`指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
效果
总结
点击button,先通过useRef
创建一个ref对象inputEl
,然后再将inputEl
赋值给input
的ref
,最后,通过inputEl.current.focus()
就可以让input聚焦。 然后,我们再想下,如果input不是个普通的dom元素,而是个组件,该怎么办呢? 这就牵扯到另外一个api:forwardRef
。
三、forwardRef
const TextInput = forwardRef((props,ref) => { return <input ref={ref}></input> })
const App = ()=> {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current`指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<TextInput ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
总结
看到React.forwardRef 接受一个渲染函数,其接收 props 和 ref 参数并返回一个 React 节点。 这样我们就将父组件中创建的ref
转发进子组件,并赋值给子组件的input元素,进而可以调用它的focus方法。 至此为止,通过useRef+forwardRef,我们就可以在函数式组件中使用ref了。当然,这篇文章还远不止如此,下面还要介绍两个重要的知识点useImperativeHandle
和回调Ref
,结合上面两个api,让你的代码更加完美。
四、useImperativeHandle
有时候,我们可能不想将整个子组件暴露给父组件,而只是暴露出父组件需要的值或者方法,这样可以让代码更加明确。而useImperativeHandle
Api就是帮助我们做这件事的。
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
可以让你在使用 ref 时自定义暴露给父组件的实例值。
例子
const TextInput = forwardRef((props,ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />
})
const App = ()=> {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<TextInput ref={inputEl}></TextInput>
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
总结
这样,我们也可以使用current.focus()来事input聚焦。这里要注意的是,子组件TextInput中的useRef对象,只是用来获取input元素的,大家不要和父组件的useRef混淆了。