Refs 的产生
官方描述: 在典型的
React
数据流中,props
是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的props
来重新渲染它。但是,在某些情况下,你需要在典型数据流之外强制修改子组件。
Refs 使用场景
- 管理焦点,文本选择或媒体播放。
- 触发强制动画。
- 集成第三方
DOM
库。
注:⚠️ 避免使用
refs
来做任何可以通过声明式实现来完成的事情。如:避免在Modal
组件内暴露open()
和close()
方法,可以通过声明属性visiable
来进行控制。
Ref 的四种方式
React v16.3
版本之前使用字符串
(string ref)或回调函数
(callback ref)的方式React v16.3
版本引入React.createRef()
API 方式React v16.8
版本引入React.useRef()
hooks 方式
注意:⚠️ 我们不建议使用
string
类型的refs
方式,它存在 一些问题 并已过时可能会在未来的版本被移除。建议用回调函数
或createRef
API 的方式代替。
string ref 字符串方式
使用示例:
import React from 'react';
class App extends React.Component {
componentDidMount() {
// 获取实例
console.log(this.refs.inputRef);
}
render() {
return (
<input ref='inputRef' />
)
}
}
callback ref 回调函数方式
使用示例:
import React from 'react';
class App extends React.Component {
componentDidMount() {
// 获取实例
console.log(this.inputRef);
}
callbackRef() {
this.inputRef = node;
}
render() {
return (
// 内联回调函数形式
<input ref={ node => this.inputRef = node } />
// 组件方法回调函数形式
<input ref={this.callbackRef} />
)
}
}
注意:⚠️ 内联的方式赋值,在组件发生了更新时,
ref
都会重新创建, 可以将回调函数作为组件的方法来避免
React.createRef() API 方式
ref
的值根据节点的类型而有所不同:
- 当
ref
属性用于 HTML 元素时,构造函数中使用React.createRef()
创建的ref
接收底层 DOM 元素作为其current
属性。 - 当
ref
属性用于自定义 class 组件时,ref
对象接收组件的挂载实例(该自定义函数组件或内部的某个节点)作为其current
属性。 - 你不能在函数组件上使用
ref
属性,因为他们没有实例。
使用示例:
import React from 'react';
class App extends React.Component {
construct(props) {
super(props);
// 创建 Refs
this.inputRef = React.createRef();
this.childRef = React.createRef();
}
componentDidMount() {
// 获取 input 实例
console.log(this.inputRef.current);
// 获取 Child 实例
console.log(this.childRef.current);
}
render() {
return (
<>
<input ref={this.inputRef} />
<Child ref={this.childRef} />
</>
)
}
}
React.useRef() Hook 方式
使用示例:
import React from 'react';
const App = () => {
const inputRef = React.useRef(null);
React.Effect(() => {
// 获取实例
console.log(inputRef.current);
}, [])
return (
<input ref={inputRef} />
)
}
Refs 转发
概念
Ref
转发是一项将ref
自动地通过组件传递到其一子组件的技巧
如何将 ref 暴露给父组件?
官方说明: 如果你使用
16.3
或更高版本的React
, 这种情况下我们推荐使用ref
转发。Ref
转发使组件可以像暴露自己的ref
一样暴露子组件的ref
。
React.forwardRef
React.forwardRef
用于refs
转发,将父组件的ref
转发给相应的子组件,一般和下面的useImperativeHandle
配合使用
useImperativeHandle【避免暴露过多属性给父组件,只能用于函数组件】
useImperativeHandle(ref, createHandle, [deps])
类组件使用示例:
- 简单示例:
// 子组件
const FancyInput(props, ref) {
const inputRef = useRef(null); // input 组件实例
useImperativeHandle(ref, () => ({
focus: () => { inputRef.current.focus() } // 暴露给父组件的方法
}));
return <input ref={inputRef} ... />;
}
export default React.forwardRef(FancyInput);
export default class Parents extends React.Component {
handleSubmit = () => {
console.log(this.testRef); // input 组件实例(只能获取到实例暴露的 focus 方法)
}
render() {
return (
<FancyInput ref={(dom) => { this.testRef = dom }} />
)
}
};
- 高阶函数组件示例:
// 子组件
const FancyInput(props) {
const { forwardRef } = props;
return <input ref={forwardRef} />;
}
// 高阶函数
const HOCFun = (WrapperedComponent) => {
// 高阶函数内部类组件可以处理一些相同逻辑并返回被包裹的组件 WrapperedComponent
class Test extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return (
<WrapperedComponent ref={forwardRef} {...rest} />
)
}
}
// 将父级传递过来的 ref 当作 props 属性传递给子组件 Test
return React.forwardRef((props, ref) => {
return <Test forwardRef={ref} {...props} />
});
}
// 渲染函数返回的组件是经过高阶函数一系列处理后的返回的包裹组件 WrapperedComponent(FancyInput)
const HocComponent = HOCFun(FancyInput);
export default class Parents extends React.Component {
handleSubmit = () => {
console.log(this.testRef); // input 组件实例
}
render() {
return (
<HocComponent ref={(node) => { this.testRef = node }} />
)
}
};
函数组件使用示例:
// 子组件
const FancyInput(props, ref) {
const inputRef = useRef(null);
// 暴露给父组件 Parents
useImperativeHandle(ref, () => ({
focus: () => { inputRef.current.focus(); }
}));
return <input ref={inputRef} ... />;
}
export default forwardRef(FancyInput);
// 父组件
const Parents = () => {
const inputRef = React.useRef(null);
useEffEct(() => {
console.log(inputRef); // input 组件实例(只能获取到实例暴露的 focus 方法)
}, [])
return (
<FancyInput ref={inputRef} />
)
}
export default Parents;
参考文章:
Forwarding Refs
Refs and the DOM
Hooks API Reference
React Ref 其实是这样的
React Hooks