闽公网安备 35020302035485号
const ref = useRef()
<InputNumber ref={ref} />
ref.current.focus()
在这个案例里,我们的目标是直接获取到 input 对象,并且调用它的 focus 方法,让 input 获得焦点。var input = document.getElementById('input')
input.focus()
但是在模块的划分上,input 元素本身并不属于当前模块/组件,因此,调用 input 方法的行为,其实是属于一种混乱。除非我们不做解耦和封装。因此,在 React 的组件封装中,并不支持直接获取到 input 的引用,而是以一种传入控制器的方式来调用它。在这个场景里:import { forwardRef } from 'react';
function MyInput(props, ref) {
// ...
}
const InputNumber = forwardRef(MyInput);
这里需要注意的是,我们需要把 ref 放在自定义组件的参数中function MyInput(props, ref) {
// ...
}
forwardRef 返回一个可渲染的函数组件。因此,我们可以通过一个简单的案例完善上面的代码function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
}
const InputNumber = forwardRef(MyInput)
所以呢,在 React 的开发中,forwardRef 能够帮助我们实现更良好的解耦,这也是我一直非常喜欢使用 forwardRef 的原因。function MyInput(props) {
return (
<label>
{props.label}
<input ref={props.ref} />
</label>
);
}
其次,代码这样写了之后,就可以直接在父组件中,通过 ref 拿到 input 的控制权function Index() {
const input = useRef(null)
function __clickHandler() {
input.current.focus()
}
return (
<div>
<button onClick={__clickHandler}>
点击获取焦点
</button>
<MyInput ref={input} />
</div>
)
}
在父组件中的使用与以前一样,但是子组件由于不再需要 forwardRef,变得更加简单了。3.deps: 依赖项数组,可选。state,props 以及内部定义的其他变量都可以作为依赖项,React 内部会使用 Object.is 来对比依赖项是否发生了变化。依赖项发生变化时,createHandle 会重新执行,ref 引用会更新。如果不传入依赖项,那么每次更新 createHandle 都会重新执行

<> <button>Write a comment</button> <Post /> </>我们期望点击按钮时,信息部分的输入框自动获取焦点,信息部分的信息展示区域能滚动到最底部,因此整个页面组件的代码可以表示为如下:
import { useRef } from 'react';
import Post from './Post.js';
export default function Page() {
const postRef = useRef(null);
function handleClick() {
postRef.current.scrollAndFocusAddComment();
}
return (
<>
<button onClick={handleClick}>
Write a comment
</button>
<Post ref={postRef} />
</>
);
}
再来思考信息部分。信息部分 Post 又分为两个部分,分别是信息展示部分与信息输入部分。此时这两个部分的 ref 要透传给 Post,并最终再次透传给页面组件。因此他们的组件结构应该长这样<>
<article>
<p>Welcome to my blog!</p>
</article>
<CommentList ref={commentsRef} />
<AddComment ref={addCommentRef} />
</>
这个时候,有一个需要考虑的点就是,有两个对象需要被控制,因此我们需要借助 useImperativeHandle 来自定义控制器,并在控制的方法中,整合他们useImperativeHandle(ref, () => {
return {
scrollAndFocusAddComment() {
commentsRef.current.scrollToBottom();
addCommentRef.current.focus();
}
};
}, []);
ref 的传递,只需要把它看成是一个普通的 props 即可,因此,Post 组件完整代码如下const Post = ({ref}) => {
const commentsRef = useRef(null);
const addCommentRef = useRef(null);
// 堆代码 duidaima.com
useImperativeHandle(ref, () => {
return {
scrollAndFocusAddComment() {
commentsRef.current.scrollToBottom();
addCommentRef.current.focus();
}
};
}, []);
return (
<>
<article>
<p>Welcome to my blog!</p>
</article>
<CommentList ref={commentsRef} />
<AddComment ref={addCommentRef} />
</>
);
}
同样的道理,我们只需要把 CommentList 与 AddComment 的 ref 传递出来就可以了。所以信息展示部分 CommentList 组件的代码为import { useRef, useImperativeHandle } from 'react';
const CommentList = ({ref}) => {
const divRef = useRef(null);
useImperativeHandle(ref, () => {
return {
scrollToBottom() {
const node = divRef.current;
node.scrollTop = node.scrollHeight;
}
};
}, []);
let comments = [];
for (let i = 0; i < 50; i++) {
comments.push(<p key={i}>Comment #{i}</p>);
}
return (
<div className="CommentList" ref={divRef}>
{comments}
</div>
);
};
export default CommentList;
信息输入部分 AddComment 的代码为function AddComment(props) {
return (
<input
placeholder="Add comment..."
ref={props.ref}
/>
)
};
export default AddComment;
与之前相比,确实要简单了许多。