Refs
Refs 用于返回对元素的引用. 但在大多数情况下, 应该避免使用它们. 当需要直接访问 DOM 元素或组件的实例时, 它们可能非常有用:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.k
Ref 通过将 Fiber 树中的 instance 赋给 ref.current 实现
function commitAttachRef(finishedWork: Fiber) {
// finishedWork 为含有 Ref effectTag 的 Fiber
const ref = finishedWork.ref
// 含有 ref prop, 这里是作为数据结构
if (ref !== null) {
// 获取 ref 属性对应的 Component 实例
const instance = finishedWork.stateNode
let instanceToUse
switch (finishedWork.tag) {
case HostComponent:
// 对于 HostComponent, 实例为对应 DOM 节点
instanceToUse = getPublicInstance(instance)
break
default:
// 其他类型实例为 fiber.stateNode
instanceToUse = instance
}
// 赋值 ref
if (typeof ref === 'function')
ref(instanceToUse)
else ref.current = instanceToUse
}
}
class CssThemeProvider extends React.PureComponent<Props> {
private rootRef = React.createRef<HTMLDivElement>()
render() {
return <div ref={this.rootRef}>{this.props.children}</div>
}
}
String Refs
不建议使用 String Refs:
- React 无法获取
this引用, 需要持续追踪当前render出的组件, 性能变慢. String Refs不可组合化: if library puts ref on passed child, user can't put another ref on it.Callback Refsare perfectly composable.String Refsdon't work with static analysis:Flowcan't guess the magic that framework does to make string ref appear onthis.refs, as well as its type (which could be different).Callback Refsare friendly to static analysis.- Starting in React 19,
String Refswas removed.
class Foo extends Component {
render() {
return <input onClick={() => this.action()} ref="input" />
}
action() {
console.log(this.refs.input.value)
}
}
class App extends React.Component<{ data: object }> {
renderRow = (index) => {
// ref 会绑定到 DataTable 组件实例, 而不是 App 组件实例上
return <input ref={`input-${index}`} />
// 如果使用 function 类型 ref, 则不会有这个问题
// return <input ref={input => this['input-' + index] = input} />;
}
render() {
return <DataTable data={this.props.data} renderRow={this.renderRow} />
}
}
Forward Refs
Before React 19, 不能在函数式组件上使用ref属性,
因为它们没有实例, 但可以在函数式组件内部使用ref.
Ref forwarding 是一个特性,
它允许一些组件获取接收到 ref 对象并将它进一步传递给子组件.
// functional component
function Button({ children }: { children: ReactElement }, ref) {
return (
<button type="button" ref={ref} className="CustomButton">
{children}
</button>
)
}
// eslint-disable-next-line react/no-forward-ref -- In React 19, 'forwardRef' is no longer necessary.
const ButtonElement = React.forwardRef(Button)
// Create ref to the DOM button:
// get ref to `<button>`
export default function App() {
const ref = useRef()
return <ButtonElement ref={ref}>Forward Ref</ButtonElement>
}
type Ref = HTMLButtonElement
interface Props {
children: React.ReactNode
}
function Button({ children }: Props, ref: Ref) {
return (
<button type="button" ref={ref} className="MyClassName">
{children}
</button>
)
}
// eslint-disable-next-line react/no-forward-ref -- In React 19, 'forwardRef' is no longer necessary.
const FancyButton = React.forwardRef<Ref, Props>(Button)
export default FancyButton
Prop Refs
Starting in React 19, you can now access ref as a prop for function components:
function MyInput({ placeholder, ref }) {
return <input placeholder={placeholder} ref={ref} />
}
<MyInput ref={ref} />
Callback Refs
class UserInput extends Component {
setSearchInput = (input) => {
this.input = input
}
componentDidMount() {
this.input.focus()
}
render() {
return (
<>
<input type="text" ref={this.setSearchInput} />
<button type="submit">Submit</button>
</>
)
}
}