React中如何正确传递Map结构的多个refs到子组件?
React中多Map结构Ref的正确传递方式
原组件代码
以下是拆分前的Articles组件:
const Articles = ({articles}) => { // articlesRefs - Map articles names to articles refs // articleToLiRefs - Map articles refs to li refs const [articlesRefs, articleToLiRefs] = useArticleLiRefs(articles); // Code (...) return ( <div> <nav><ul> { articles.map((article) => { const articleName = article.get('name'); const articleRef = articlesRefs.get(articleName); const liRef = articleToLiRefs.get(articleRef); return ( <li ref={liRef} key={articleName} onClick={()=> articleRef.current.scrollIntoView()}> {articleName} </li> ); })} </ul></nav> {/* Code (...) */} </div> )}
两种拆分方案分析
方案一:通过props传递Ref Map
直接将存储ref的Map对象通过props传给ArticlesNav组件:
const Articles = ({articles}) => { ... return ( ... <ArticlesNav articles={articles} articlesRefs={articlesRefs} articleToLiRefs={articleToLiRefs} />
ArticlesNav组件内部复用原nav标签内的代码。
结论:该方案完全正确
虽然常说不建议直接传递ref,但这里传递的是存储ref的Map容器,而非单个React ref对象本身(Map是普通JavaScript对象),React不会对这类props做特殊处理,因此完全符合props的使用场景。该方案简单直观,维护成本低,是优先选择的方案。
方案二:用forwardRef传递包含多个Ref的useRef对象
父组件中用useRef包装Ref Map,再通过forwardRef传递:
const Articles = ({articles}) => { ... const refsToForward = useRef([articlesRefs, articleToLiRefs]); ... return ( ... <ArticlesNav articles={articles} ref={refsToForward} />
子组件内部获取:
const ArticlesNav = forwardRef( ({articles}, ref ) => { const [articlesRefs, articleToLiRefs] = ref.current;
结论:该方案存在问题,不推荐
- useRef的current值不会随渲染自动更新:当articles变化导致
useArticleLiRefs返回新的Map时,refsToForward.current会保留旧值,导致ArticlesNav使用过期的Ref Map,功能异常。 - 滥用forwardRef:forwardRef的设计初衷是让父组件直接访问子组件的DOM节点或内部实例,而非传递数据型的Ref集合,属于场景误用。
如果非要用这种方式(不推荐),需要手动更新ref的current值:
const [articlesRefs, articleToLiRefs] = useArticleLiRefs(articles); const refsToForward = useRef([articlesRefs, articleToLiRefs]); useEffect(() => { refsToForward.current = [articlesRefs, articleToLiRefs]; }, [articlesRefs, articleToLiRefs]);
总结
优先选择方案一,通过props传递存储ref的Map对象,这是最简单、可靠的实现方式。方案二属于不必要的复杂实现,且存在潜在的更新不同步问题,不建议使用。
内容的提问来源于stack exchange,提问作者Przemek
相关产品推荐
相关产品推荐

