React
forwardRef 把子组件的命令式能力交给父组件。
在线预览
等待操作
组件关系与数据流
ComponentRef / DOM
差异焦点函数组件不会自动暴露实例,需要 forwardRef 和 useImperativeHandle。
实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。
flowchart TB
accTitle: React forwardRef 命令式句柄
accDescr: SearchInput 必须通过 forwardRef 接收 ref,再用 useImperativeHandle 构造受控句柄;ReactDemo 调用句柄时最终操作 input DOM。
parent["ReactDemo"] -->|"ref prop"| child["SearchInput + forwardRef"]
child --> expose{{"useImperativeHandle 构造句柄"}}
expose --> handle[("SearchInputHandle")]
parent -.->|"focus() / select()"| handle
handle --> dom[("input DOM")]
class parent,child component
class expose process
class handle,dom resource
classDef state fill:#ffffff,stroke:#149eca,color:#172033,stroke-width:1.2px
classDef component fill:#e5f6fb,stroke:#149eca,color:#172033,stroke-width:2px,font-weight:600
classDef process fill:#f4fbfd,stroke:#149eca,color:#172033,stroke-width:1.6px
classDef resource fill:#f3f5f8,stroke:#778195,color:#172033,stroke-width:1.4pxforwardRef 接收 ref,useImperativeHandle 显式构造父组件可调用的句柄。
实际运行源码
import { forwardRef, useImperativeHandle, useRef, useState } from "react";
type SearchInputHandle = { focus: () => void; select: () => void;};
const SearchInput = forwardRef<SearchInputHandle>(function SearchInput(_, ref) { const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus(), select: () => inputRef.current?.select(), }), []);
return ( <label className="demo-field"> 子组件输入框 <input ref={inputRef} className="demo-input" defaultValue="React forwardRef" /> </label> );});
export function ReactDemo() { const searchRef = useRef<SearchInputHandle>(null); const [action, setAction] = useState("等待操作");
return ( <div className="demo-stack"> <SearchInput ref={searchRef} /> <div className="demo-actions"> <button className="demo-button" type="button" onClick={() => { searchRef.current?.focus(); setAction("已聚焦输入框"); }} > 聚焦 </button> <button className="demo-button" type="button" onClick={() => { searchRef.current?.select(); setAction("已全选输入内容"); }} > 全选 </button> </div> <p className="demo-result" aria-live="polite">{action}</p> </div> );}