Refs 和 DOM
Refs
提供了一种在render
方法中创建DOM 节点
或React 元素
的方式
在常规的 React 数据流中,props 是父组件与子组件交互的唯一方式
。要修改子元素,你需要用新的props
去重新渲染子元素。
Refs
:强制修改子元素
。被修改的子元素可以是
React组件实例
(类组件实例),或者是一个DOM 元素
,不能使函数组件
何时使用 Refs
- 处理
focus
、文本选择
或者媒体播放
- 触发强制
动画
- 集成第三方
DOM库
- 如果可以通过声明式实现,就尽量避免使用
refs
不要过度使用 Refs
能使用
状态提升
运用state
改变组件状态,不要使用refs
来更新组件
创建 Refs: React.createRef()
使用
React.createRef()
创建refs
,通过ref
属性来获得 子元素。当构造组件时,
refs
通常被赋值给实例的一个属性,这样你可以在组件中任意一处使用它们.
1 | class MyComponent extends React.Component { |
访问 Refs: ref.current
ref
属性被传递给一个render
函数中的元素时,可以使用ref
中的current
属性对节点的引用进行访问。
1 | const node = this.myRef.current; |
ref的值取决于节点的类型:
- 当
ref
属性被用于一个普通的HTML
元素时,React.createRef()
将接收底层DOM 元素
作为它的current
属性以创建ref
。 - 当
ref
属性被用于一个自定义类组件
时,ref
对象将接收该组件已挂载的实例
作为它的current
。 不能在函数式组件上
使用ref
属性,因为它们没有实例
React
组件在加载时将DOM
元素传入ref
的回调函数,在卸载时则会传入null
。在componentDidMount
或componentDidUpdate
这些生命周期回调之前执行ref
回调
在 DOM 元素上添加 Ref
1 | class CustomTextInput extends React.Component { |
类(Class)组件 添加Ref
包装上面的
CustomTextInput
,来模拟挂载之后立即被点击:使用ref
来访问自定义组件,并手动调用它的focusTexInput
方法
这种方法仅对以类(class)声明的 CustomTextInput 有效
当ref是类组件实例时,可以直接获取实力上的属性及方法
1 | //上面的CustomTextInput组件不再重复写 |
Refs 与 函数式组件
不能在函数式组件上使用 ref
属性,因为它们没有实例,如果需要使用 ref
,就需要将组件转化成 类(class
)组件,就像需要 生命周期方法 或者 state
一样。
1 | //错误 |
但是可以 在函数式组件内部使用 ref 来引用一个 DOM 元素或者 类(class)组件:ref={(input) => { textInput = input; }}input就是个形参 代表当前元素,可以是任何形参名
1 | function CustomTextInput(props) { |
对父组件暴露 DOM 节点
通常
不建议从父组件访问子节点的 DOM 节点
,因为它会破坏组件的封装,但偶尔也可用于触发焦点
或测量子 DOM 节点
的大小或位置可以像上边 “类(Class) 组件添加 Ref”章节那样 先通过
refs
获得自定义组件,然后在组件内添加refs
来实现缺陷:
只能获取组件实例
而不是 DOM 节点
。并且,它还在函数式组件上无效
通常通过“Ref转发”实现
对父组件暴露DOM节点
回调 Refs
(能用 createRef不用回调refs)ref={element=>this.textInput = element}
“回调 ref”,更加细致地控制何时 ref 被设置和解除。
不同于
传递createRef()
创建的 ref 属性,它会传递一个函数
。这个函数接受React 组件的实例
或HTML DOM 元素
作为参数,以存储它们并使它们能被其他地方访问。
回调ref``不需要通过 current
属性访问,直接this.textInput.focus()
;
1 | class CustomTextInput extends React.Component { |
在组件间通过props传递回调形式的 refs(父组件获取子组件元素)
Parent
通过props
将ref 回调函数
作为inputRef属性
传递给CustomTextInput组件
,然后CustomTextInput组件
通过ref
属性将其传递给<input>
元素。最终,
Parent
中的this.inputElement
将被设置为与CustomTextIput组件
中的<input>
元素相对应的DOM 节点
1 | function CustomTextInput (props) { |
1 | class Parent extends React.Component{ |
转发 Refs
Ref
转发是一种自动将ref
通过组件传递给子组件的技术
Ref
转发是一种选择性加入的功能,可让某些组件接收他们收到的ref
,并将其向下传递(换句话说,“转发”)给子组件。
React.forwardRef((props,ref)=>())
接收props,ref
作为参数第二个
ref
参数仅在使用 React.forwardRef 调用定义组件时才存在
,常规函数或类组件不接收ref
参数
Ref
转发不限于 DOM 组件
。也可以将refs
转发给类组件实例
。
FancyButton 使用 React.forwardRef
来获取传递给它的 ref , 然后将其转发给它渲染的的 DOM button:
1 | //3. ref转发 |
上面示例的步骤:
我们通过调用
React.createRef
创建一个React ref
并将其分配给ref
变量。通过将
ref
变量传递给指定ref
为JSX
属性的<FancyButton ref={ref}>
。
React
将ref
传递给forwardRef
中的(props, ref) => ... 函数
作为第二个参数
。我们将这个
ref
参数转发到指定ref
为JSX
属性的<button ref = {ref}>
。当附加
ref
时,ref.current
将指向<button>
DOM节点。
将父组件通过createRef创建的Ref通过props传递给子组件,子组件将传递过来的ref绑定在其元素上也能实现 ref转发
也可以:
1 | function CustomTextInput(props) { |
当使用
HOC
扩展组件时,建议使用React
的forwardRef
函数 将ref
转发到 包装组件上第三方
HOC
没有实现ref
转发, 上述模式仍然可以用作后备。