导航
作用
示例
// 简单使用
import React, { useEffect, useRef } from "react";
// 用在 class 组件
// const ref = React.createRef();
export function RefDemo() {
const ref = useRef(null);
useEffect(() => {
console.log(ref.current);
}, []);
return (
<div>
<h2 ref={ref}>Ref Demo</h2>
</div>
)
}
// 绑定多个 ref
import React, { useEffect, useRef } from "react";
interface Refs {
c1?: HTMLHeadingElement | null;
c2?: HTMLHeadingElement | null;
c3?: HTMLHeadingElement | null;
}
export function RefDemo() {
const refs = useRef<Refs>({});
useEffect(() => {
console.log(refs.current);
}, []);
return (
<div>
<h2 ref={(node) => { refs.current.c1 = node }}>Ref 1 Demo</h2>
<h2 ref={(node) => { refs.current.c2 = node }}>Ref 2 Demo</h2>
<h2 ref={(node) => { refs.current.c3 = node }}>Ref 3 Demo</h2>
</div>
)
}
作用
示例
import React, { forwardRef, useEffect, useId, useRef } from "react";
interface InputProps {};
const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const id = useId();
return (
<div style={{ display: "flex" }}>
<label htmlFor={id}>child input</label>
<input id={id} ref={ref} />
</div>
)
});
export const ForwardRefDemo = () => {
const ref = useRef<HTMLInputElement | null>(null);
useEffect(() => {
console.log(ref);
}, []);
return <Input ref={ref} />
}
useImperativeHandle 和 forwardRef 往往一起出现,但它们的职责是完全不同的层级。
React 的 useImperativeHandle 是一个 Hook,用来 自定义暴露给父组件的 ref 内容。
换句话说:
useImperativeHandle(ref, createHandle, [deps])
✅ 子组件(Child.tsx)
import React, { useImperativeHandle, useRef, forwardRef } from "react";
export interface ChildHandle {
focusInput: () => void;
reset: () => void;
}
const Child = forwardRef<ChildHandle>((_, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
// 暴露给父组件的函数
useImperativeHandle(ref, () => ({
focusInput() {
inputRef.current?.focus();
},
reset() {
if (inputRef.current) inputRef.current.value = "";
}
}));
return (
<div>
<input ref={inputRef} placeholder="请输入内容..." />
</div>
);
});
export default Child;
✅ 父组件(Parent.tsx)
import React, { useRef } from "react";
import Child, { ChildHandle } from "./Child";
export default function Parent() {
const childRef = useRef<ChildHandle>(null);
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current?.focusInput()}>聚焦输入框</button>
<button onClick={() => childRef.current?.reset()}>清空输入框</button>
</div>
);
}
📘 运行效果:
使用场景总结
| 场景 | 示例 |
|---|---|
| 操作子组件内部 DOM | 例如父组件要聚焦子组件内部 input |
| 调用子组件暴露的方法 | 例如子组件提供 reset、validate、scrollToBottom 等 |
| 封装复杂组件对外提供统一接口 | 例如封装富文本编辑器组件暴露 getHTML()、setHTML() 方法 |
用于异步加载组件(即懒加载)
React Suspense 的工作原理基于一种新的渲染模式,称为“可中断的渲染”(Interruptible Rendering)。这一模式允许 React 暂停在某些组件上的渲染,并在这些组件准备好之后继续渲染。
以下是 Suspense 的核心机制:
异步边界
当 React 渲染一个组件时,如果它遇到一个 Suspense 组件,并且 fallback 触发了,则会暂停该组件树的渲染。
资源的异步加载
React.lazy 用于异步加载 React 组件。这些组件会在它们的模块加载完成之前暂停渲染。
数据的异步获取
Suspense 可以与数据获取库结合使用。在这种情况下,React 会等待数据加载完成后再继续渲染。
缓存机制
React 的新架构使用缓存来存储加载的数据或组件。这确保了相同的数据或组件在不同的渲染周期中可以被复用,减少了不必要的加载。
协调(Reconciliation) 和优先级调度
React 的调度器会根据任务的优先级来协调更新。高优先级的任务(如用户交互)会优先处理,而低优先级的任务(如数据获取)会被延迟处理。这种机制使得 React 可以在任务之间进行切换,提高应用的响应速度和性能。
一个完整的示例,包括: ✅ 懒加载 Header.tsx 和 Footer.tsx ✅ 使用 React.lazy ✅ 使用 Suspense 包裹 ✅ 含 fallback(加载中提示)
目录结构
src/
├── App.tsx
├── Header.tsx
├── Footer.tsx
└── main.tsx
Header.tsx
export default function Header() {
return <header style={{ background: "#eee", padding: "10px" }}>🌟 Header</header>;
}