一、ref
ref是React提供的用来操纵React组件实例或者DOM元素的接口。表示为对组件真正实例的引用,其实就是ReactDOM.render()返回的组件实例。
ref
可以挂到任何元素上,可以挂到组件上也可以挂载到DOM元素上。
Class组件中使用ref:
在React的Class组件时期,我们通过createRef
创建ref。
class MyComponent extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); } render() { return <input type="text" ref={this.inputRef} />; } componentDidMount() { this.inputRef.current.focus(); } }
在这个例子里ref挂到了原生DOM元素<input />
,在这种情况下可以通过ref.current
获取到这个DOM元素,并直接调用上面的方法。
ref如果挂在到一个Class组件上,这样ref.current
获取到的就是这个Class组件的实例。
函数式组件中使用ref:
但是,ref不能挂到一个函数式组件(除非使用forwardRef
),因为:ref回调函数会在组件被挂载之后将组件实例传递给函数,函数式组件没有实例。
在函数式组件中通过useRef创建ref。
function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
二、createRef和useRef的区别
createRef 只能用在class组件中,useRef 只能用在函数式组件中。
createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。
如果在函数式组件中使用createRef
创建的ref
,其值会随着函数式组件的重新执行而不断初始化。hooks不能用在class组件中,所以class组件只能使用createRef。
三、forwardRef
前面我们说到:ref不能挂到一个函数式组件(除非使用forwardRef
)。
forwardRef
可以直接包裹一个函数式组件,被包裹的函数式组件会获得被分配给自己的ref(作为第二个参数)。
如果你直接将ref
分配给没有被forwardRef
包裹的函数式组件,React会在控制台给出错误。
const App: React.FC = () => { const ref = useRef(null); useEffect(() => { ref.current.focus(); }, []); return ( <> <Child ref={ref} /> </> ); }; const Child = forwardRef((props, ref: Ref<any>) => { return <input type="text" name="child" ref={ref} />; });
**注意:React.forwardRef参数必须是function,而这个API通常用来解决HOC(高阶组件)中丢失ref的问题。
四、useImperativeHandle
在forwardRef
例子中的代码实际上是不推荐的,因为无法控制要暴露给父组件的值,所以我们使用useImperativeHandle
控制要将哪些东西暴露给父组件。
useImperativeHandle
应当与 forwardRef
一起使用:
调用方式:
useImperativeHandle(ref, createHandle, [deps])
- 接收一个
ref
- 接收一个函数,这个函数返回的对象即是要暴露出的
ref
- 类似
useEffect
,接收一个依赖数组
const FancyInput=(props, ref) =>{ const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} />; } export default forwardRef(FancyInput);
在本例中,渲染 <FancyInput ref={inputRef} />
的父组件可以调用 inputRef.current.focus()