React Refs 完整使用指南
1. Refs 基础用法
1.1 创建和访问 Refs
// 类组件中使用 createRef
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
// 访问 DOM 节点
console.log(this.myRef.current);
}
render() {
return <div ref={this.myRef}>Hello</div>;
}
}
// 函数组件中使用 useRef
function MyFunctionComponent() {
const myRef = useRef(null);
useEffect(() => {
// 访问 DOM 节点
console.log(myRef.current);
}, []);
return <div ref={myRef}>Hello</div>;
}
1.2 回调 Refs
class CallbackRefComponent extends React.Component {
setTextInputRef = (element) => {
this.textInput = element;
};
focusTextInput = () => {
// 直接使用原生 DOM API
if (this.textInput) this.textInput.focus();
};
render() {
return (
<>
<input type="text" ref={this.setTextInputRef} />
<button onClick={this.focusTextInput}>
Focus the text input
</button>
</>
);
}
}
2. Refs 常见用途
2.1 管理焦点
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// 组件挂载时自动聚焦
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={() => inputRef.current.focus()}>
Focus Input
</button>
</div>
);
}
2.2 文本选择
function TextSelection() {
const textRef = useRef(null);
const selectText = () => {
if (textRef.current) {
textRef.current.select();
}
};
return (
<div>
<input
ref={textRef}
type="text"
defaultValue="Click to select me"
/>
<button onClick={selectText}>Select Text</button>
</div>
);
}
2.3 媒体控制
function VideoPlayer() {
const videoRef = useRef(null);
const handlePlay = () => {
videoRef.current.play();
};
const handlePause = () => {
videoRef.current.pause();
};
return (
<div>
<video ref={videoRef}>
<source src="video.mp4" type="video/mp4" />
</video>
<button onClick={handlePlay}>Play</button>
<button onClick={handlePause}>Pause</button>
</div>
);
}
3. Refs 转发
3.1 基本 Ref 转发
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="fancy-button">
{props.children}
</button>
));
// 使用转发的 ref
function Parent() {
const buttonRef = useRef(null);
useEffect(() => {
console.log(buttonRef.current); // 访问 button DOM 节点
}, []);
return <FancyButton ref={buttonRef}>Click me!</FancyButton>;
}
3.2 高阶组件中的 Ref 转发
function withLogger(WrappedComponent) {
class LoggerComponent extends React.Component {
componentDidUpdate(prevProps) {
console.log('Props updated', prevProps, this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
return <WrappedComponent ref={forwardedRef} {...rest} />;
}
}
return React.forwardRef((props, ref) => {
return <LoggerComponent {...props} forwardedRef={ref} />;
});
}
// 使用高阶组件
const ButtonWithLogger = withLogger(FancyButton);
4. Refs 与函数组件
4.1 useImperativeHandle 使用
const FancyInput = React.forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
},
setValue: (value) => {
inputRef.current.value = value;
}
}));
return <input ref={inputRef} />;
});
// 使用自定义 ref 方法
function Parent() {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus();
inputRef.current.setValue('Hello!');
console.log(inputRef.current.getValue());
};
return (
<div>
<FancyInput ref={inputRef} />
<button onClick={handleClick}>Manipulate Input</button>
</div>
);
}
5. Refs 的高级用法
5.1 多个 Refs 管理
function MultipleRefs() {
const refs = useRef({});
const setRef = (id) => (element) => {
refs.current[id] = element;
};
const focusRandom = () => {
const keys = Object.keys(refs.current);
const randomKey = keys[Math.floor(Math.random() * keys.length)];
refs.current[randomKey]?.focus();
};
return (
<div>
<input ref={setRef('input1')} placeholder="Input 1" />
<input ref={setRef('input2')} placeholder="Input 2" />
<input ref={setRef('input3')} placeholder="Input 3" />
<button onClick={focusRandom}>Focus Random Input</button>
</div>
);
}
5.2 条件性 Refs
function ConditionalRef() {
const [showInput, setShowInput] = useState(true);
const inputRef = useRef(null);
useEffect(() => {
if (showInput) {
inputRef.current?.focus();
}
}, [showInput]);
return (
<div>
<button onClick={() => setShowInput(!showInput)}>
Toggle Input
</button>
{showInput && <input ref={inputRef} />}
</div>
);
}
6. Refs 最佳实践
6.1 避免过度使用
// 不推荐
function BadExample() {
const divRef = useRef(null);
const updateContent = () => {
// 不推荐直接操作 DOM
divRef.current.innerHTML = 'Updated content';
};
return <div ref={divRef}>Content</div>;
}
// 推荐
function GoodExample() {
const [content, setContent] = useState('Content');
return <div>{content}</div>;
}
6.2 清理 Refs
function CleanupExample() {
const timerRef = useRef(null);
useEffect(() => {
timerRef.current = setInterval(() => {
console.log('Tick');
}, 1000);
// 清理定时器
return () => {
clearInterval(timerRef.current);
};
}, []);
return <div>Timer Example</div>;
}
7. 总结
Refs 使用要���:
-
适用场景:
- DOM 元素的直接操作
- 媒体播放控制
- 文本选择和焦点管理
- 第三方 DOM 库的集成
-
注意事项:
- 避免过度使用 Refs
- 优先使用声明式编程
- 及时清理 Refs 资源
- 谨慎使用 Refs 操作 DOM
-
性能考虑:
- Refs 更新不会触发重新渲染
- 适当使用可以避免不必要的渲染
- 大量 Refs 可能影响内存使用
-
开发建议:
- 优先使用 React 的声明式 API
- 只在必要时使用 Refs
- 使用 TypeScript 增加类型安全
- 保持代码的可维护性
</rewritten_file>