目录
1. Refs
1.1. Refs 适合干什么
1.2. 创建、添加、访问 Refs
1.3. Callback Refs
2. Forwarding Refs
3. Hooks
3.1. useRef
3.2. useImperativeHandle
1. Refs
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
勿过度使用 Refs
1.1. Refs 适合干什么
- 管理焦点,文本选择或媒体播放。
- 触发强制动画。
- 与 ActiveX 控件交互。
- 集成第三方 DOM 库。
1.2. 创建、添加、访问 Refs
代码语言:javascript复制// 创建 Refs
this.myRef = React.createRef();
// 添加 Refs
<SomeClassComponent ref={this.myRef}/>
<someHtmlElement ref={this.myRef}/>
// 引用 Refs
const node = this.myRef.current;
- ref 用于 HTML 元素时,ref.current 指向 DOM 元素。
- ref 用于自定义 class 组件时,ref.current 指向组件的挂载实例。
- ref 不能用在 function 组件上(因为 fucntion 组件没有实例)。
示例1:ref->DOM
代码语言:javascript复制import React from "react"
class HelloWorld extends React.Component{
constructor(props) {
super(props);
this.myRef = React.createRef<HTMLInputElement>();
}
componentDidMount(): void {
this.myRef.current.value = "HelloWorld!";
this.myRef.current.focus();
}
render() {
return <input ref={this.myRef} type={"text"} />;
}
}
export default function App(){
return (
<HelloWorld/>
);
}
示例2:ref->class 组件
代码语言:javascript复制import React from "react"
class HelloWorld extends React.Component{
constructor(props) {
super(props);
this.myRef = React.createRef();
}
changeBgColor(bgcolor): void {
this.myRef.current.style.backgroundColor = bgcolor;
}
render() {
return <input ref={this.myRef} type={"text"} />;
}
}
export default class App extends React.Component{
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount(): void {
this.myRef.current.changeBgColor("purple");
}
render() {
return <HelloWorld ref={this.myRef}/>;
};
}
1.3. Callback Refs
除了使用 createRef() 创建的 ref 属性,React 也支持通过 callback 设置 refs。
示例:
代码语言:javascript复制import React from "react"
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// 使用原生 DOM API 使 text 输入框获得焦点
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// 组件挂载后,让文本框自动获得焦点
this.focusTextInput();
}
render() {
// 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
// 实例上(比如 this.textInput)
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
export default function App(){
return <CustomTextInput/>
}
示例2:Parent direct visit child's DOM
代码语言:javascript复制import React from "react"
function CustomTextInput(props) {
return (
<div>
{/* eslint-disable-next-line react/prop-types */}
<input ref={props.inputRef}/>
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = null;
}
focusTextInput = () => {
if (this.inputElement) {
this.inputElement.focus();
}
}
saveInputRef = (el) => {
this.inputElement = el;
}
render() {
return (
<div>
<CustomTextInput inputRef={this.saveInputRef}/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
export default function App() {
return <Parent/>
}
Parent 把它的 refs 回调函数当作 inputRef props 传递给了 CustomTextInput,而且 CustomTextInput 把相同的函数作为特殊的 ref 属性传递给了 <input>。结果是,在 Parent 中的 this.inputElement 会被设置为与 CustomTextInput 中的 input 元素相对应的 DOM 节点。
2. Forwarding Refs
Ref 转发允许某些组件接收 ref,并将其向下传递(或者说“转发”)给子组件。
勿过度使用 Refs
API:
代码语言:javascript复制React.forwardRef
示例1:React.forwardRef
代码语言:javascript复制import React from "react"
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
export default class App extends React.Component{
constructor(props) {
super(props);
this.ref = React.createRef();
}
componentDidMount(): void {
console.log(this.ref.current);
}
render(){
return <FancyButton ref={this.ref}>Click me!</FancyButton>;
}
}
- 我们通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
- 我们通过指定 ref 为 JSX 属性,将其向下传递给 <FancyButton ref={ref}>。
- React 传递 ref 给 forwardRef 内函数 (props, ref) => ...,作为其第二个参数。
- 我们向下转发该 ref 参数到 <button ref={ref}>,将其指定为 JSX 属性。
- 当 ref 挂载完成,ref.current 将指向 <button> DOM 节点。
示例2:HOC Forwarding Refs
代码语言:javascript复制function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
示例3:HOC Forwarding Refs React Dev Tools
React.forwardRef 接受一个渲染函数。React DevTools 使用该函数来决定为 ref 转发组件显示的内容。
代码语言:javascript复制import React from "react"
class FancyButton extends React.Component {
focus() {
// ...
}
render(){
return "FancyButton";
}
}
// HOC
function logProps(Component) {
class LogProps extends React.Component {
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 如果命名了渲染函数,DevTools 也将包含其名称(例如 “ForwardRef(myFunction)”)
function myForwardRef(props, ref){
return <LogProps {...props} forwardedRef={ref} />;
}
return React.forwardRef(myForwardRef);
}
// HOC(FancyButton)
const WrappedFancyButton = logProps(FancyButton);
export default class App extends React.Component{
constructor(props) {
super(props);
this.ref = React.createRef();
}
componentDidMount(): void {
console.log(this.ref.current);
}
render(){
return <WrappedFancyButton ref={this.ref}/>;
}
}
3. Hooks
勿过度使用 Refs
3.1. useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
示例:
代码语言:javascript复制import React, {useRef} from "react"
function HelloWorld(){
const myRef = useRef(null);
const onButtonClick = () => {
myRef.current.value = "HelloWorld!";
myRef.current.focus();
}
return (<>
<input ref={myRef} type="text" />
<button onClick={onButtonClick}>Click</button>
</>);
}
export default function App(){
return (
<HelloWorld/>
);
}
3.2. useImperativeHandle
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。useImperativeHandle 应当与 React.forwardRef 一起使用:
示例:
代码语言:javascript复制import React, {useRef, useEffect, useImperativeHandle} from "react"
function HelloWorld(props, ref){
const myRef = useRef(null);
useImperativeHandle(ref, ()=>({
changeBgColor(bgcolor) {
myRef.current.style.backgroundColor = bgcolor;
}
}));
return <input ref={myRef} type={"text"} />;
}
HelloWorld = React.forwardRef(HelloWorld);
export default function App(){
const myRef = useRef(null);
useEffect(()=>{
myRef.current.changeBgColor("purple");
});
return <HelloWorld ref={myRef}/>;
}
参考:
https://react.docschina.org/docs/hooks-reference.html#useref