如何在React+TypeScript中向children传递render props?
解决Accordion组件传递open属性给子组件的TypeScript类型问题
你的核心问题是当前Accordion定义的children为render prop函数,但App中传入的是普通React元素,类型不兼容,同时需要把open属性传递给AccordionContent。下面提供两种可行方案:
方案一:使用Render Props模式(类型匹配写法)
这种方案需要统一组件的使用方式,让子组件通过函数接收isOpen参数。
修正Accordion.tsx的类型与逻辑
import React from 'react'; // 定义传递给子组件的参数类型 interface RenderPropParams { isOpen: boolean; } interface AccordionProps { open?: boolean; // children是接收isOpen的函数,返回React节点 children: (props: RenderPropParams) => React.ReactNode; } const Accordion = ({ open = false, children }: AccordionProps) => { // 直接调用children函数,传入open值作为isOpen return ( <div className="flex flex-col border border-gray-600"> {children({ isOpen: open })} </div> ); }; export default Accordion;
修改App.tsx的使用方式
将Accordion的children写成函数,接收isOpen后传递给AccordionContent:
const App = () => { return ( <Accordion open={true}> {({ isOpen }) => ( <> <AccordionTitle title="First Accordion" /> {/* 把isOpen传递给AccordionContent */} <AccordionContent isOpen={isOpen}> <p> Lorem Ipsum including versions of Lorem Ipsum. </p> </AccordionContent> </> )} </Accordion> ); };
给AccordionContent添加类型
interface AccordionContentProps { isOpen: boolean; children: React.ReactNode; } const AccordionContent = ({ isOpen, children }: AccordionContentProps) => { return ( <div className={isOpen ? 'block' : 'hidden'}> {children} </div> ); };
方案二:遍历子元素自动注入属性(保持App原有写法)
如果想保留App中直接嵌套子组件的写法,可以通过遍历子元素,给AccordionContent自动注入isOpen属性。
修正Accordion.tsx
import React, { ReactElement } from 'react'; // 先定义AccordionContent的属性类型 interface AccordionContentProps { isOpen?: boolean; children: React.ReactNode; } // 辅助函数:判断是否为AccordionContent组件 const isAccordionContent = (child: React.ReactNode): child is ReactElement<AccordionContentProps> => { return React.isValidElement(child) && child.type === AccordionContent; }; interface AccordionProps { open?: boolean; // children为普通React节点数组 children: React.ReactNode; } const Accordion = ({ open = false, children }: AccordionProps) => { // 遍历子元素,给AccordionContent添加isOpen属性 const transformedChildren = React.Children.map(children, (child) => { if (isAccordionContent(child)) { return React.cloneElement(child, { isOpen: open }); } return child; }); return ( <div className="flex flex-col border border-gray-600"> {transformedChildren} </div> ); }; export default Accordion;
App.tsx保持原有写法不变
const App = () => { return ( <Accordion open={true}> <AccordionTitle title="First Accordion" /> <AccordionContent> <p> Lorem Ipsum including versions of Lorem Ipsum. </p> </AccordionContent> </Accordion> ); };
给AccordionContent添加类型
interface AccordionContentProps { isOpen?: boolean; children: React.ReactNode; } const AccordionContent = ({ isOpen = false, children }: AccordionContentProps) => { return ( <div className={isOpen ? 'block' : 'hidden'}> {children} </div> ); };
两种方案对比:
- 方案一更灵活,适合需要给多个子组件传递isOpen的场景;
- 方案二更贴近你原本的写法,侵入性低,适合仅给特定子组件传递属性的场景。
内容的提问来源于stack exchange,提问作者Ajitesh Chhatani
相关产品推荐
相关产品推荐

