React 中获取 DOM 引用的方式
#react
目录
- 1. 总结
- 2. React.createRef()
- 3. useRef Hook
- 4. 回调 Refs(Callback Refs)
- 5. forwardRef
- 6. 字符串 Refs(不推荐)
- 7. findDOMNode(不推荐)
- 8. 实际应用示例
1. 总结
- 优先使用
useRef
和createRef
- 需要更细粒度控制时使用
回调refs
- 需要跨组件传递 ref 时使用
forwardRef
- 尽量避免使用
字符串 refs
和findDOMNode
- 只在必要时使用 refs(如焦点管理、动画、第三方 DOM 库集成等)
- 不要过度使用 refs 来操作 DOM,尽量通过 React 的声明式更新来管理 UI
2. React.createRef()
最新推荐的创建 ref 的方式:
// 1.1 在类组件中使用
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>;
}
}
// 1.2 在类组件中使用 class fields
class MyComponent extends React.Component {
myRef = React.createRef();
handleClick = () => {
// 访问 DOM 节点
this.myRef.current.focus();
};
render() {
return (
<>
<input ref={this.myRef} />
<button onClick={this.handleClick}>Focus Input</button>
</>
);
}
}
3. useRef Hook
在函数组件中使用的 Hook 方式:
// 2.1 基本用法
function TextInputWithFocusButton() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</>
);
}
// 2.2 多个 ref 的使用
function MultipleRefs() {
const firstRef = useRef(null);
const secondRef = useRef(null);
const thirdRef = useRef(null);
const handleClick = (ref) => {
ref.current.scrollIntoView({ behavior: 'smooth' });
};
return (
<div>
<button onClick={() => handleClick(firstRef)}>Scroll to First</button>
<button onClick={() => handleClick(secondRef)}>Scroll to Second</button>
<button onClick={() => handleClick(thirdRef)}>Scroll to Third</button>
<div ref={firstRef}>First Element</div>
<div ref={secondRef}>Second Element</div>
<div ref={thirdRef}>Third Element</div>
</div>
);
}
4. 回调 Refs(Callback Refs)
通过回调函数的方式设置 ref:
// 3.1 基本用法
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 Input</button>
</>
);
}
}
// 3.2 在函数组件中使用
function CallbackRefFunctional() {
let textInput = null;
const setTextInputRef = (element) => {
textInput = element;
};
const focusTextInput = () => {
if (textInput) textInput.focus();
};
return (
<>
<input ref={setTextInputRef} />
<button onClick={focusTextInput}>Focus Input</button>
</>
);
}
5. forwardRef
用于转发 refs 到子组件:
// 4.1 基本用法
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="fancy-button">
{props.children}
</button>
));
// 使用 FancyButton
function Parent() {
const buttonRef = useRef(null);
return <FancyButton ref={buttonRef}>Click me!</FancyButton>;
}
// 4.2 带有 HOC 的 forwardRef
const withLogger = (WrappedComponent) => {
return React.forwardRef((props, ref) => {
useEffect(() => {
console.log('Component mounted');
}, []);
return <WrappedComponent {...props} ref={ref} />;
});
};
const LoggedButton = withLogger(FancyButton);
6. 字符串 Refs(不推荐)
旧版本的 ref 方式,现已不推荐使用:
// 不推荐使用的字符串 ref
class LegacyComponent extends React.Component {
componentDidMount() {
// 不推荐
this.refs.myInput.focus();
}
render() {
return <input ref="myInput" />;
}
}
7. findDOMNode(不推荐)
legacy API,在严格模式下会警告:
// 不推荐使用 findDOMNode
class LegacyFindDOMNode extends React.Component {
componentDidMount() {
// 不推荐
const node = ReactDOM.findDOMNode(this);
}
render() {
return <div>Legacy</div>;
}
}
8. 实际应用示例
// 7.1 复杂表单处理
function ComplexForm() {
const formRefs = {
name: useRef(null),
email: useRef(null),
phone: useRef(null)
};
const validateField = (fieldName) => {
const value = formRefs[fieldName].current.value;
// 验证逻辑
};
return (
<form>
<input ref={formRefs.name} placeholder="Name" />
<input ref={formRefs.email} type="email" placeholder="Email" />
<input ref={formRefs.phone} placeholder="Phone" />
</form>
);
}
// 7.2 媒体播放器
function VideoPlayer({ src }) {
const videoRef = useRef(null);
const handlePlay = () => {
videoRef.current.play();
};
const handlePause = () => {
videoRef.current.pause();
};
return (
<div>
<video ref={videoRef} src={src} />
<button onClick={handlePlay}>Play</button>
<button onClick={handlePause}>Pause</button>
</div>
);
}