Reactjs 如何将TypeScript泛型与React渲染道具一起使用

Reactjs 如何将TypeScript泛型与React渲染道具一起使用,reactjs,typescript,typescript-generics,react-typescript,Reactjs,Typescript,Typescript Generics,React Typescript,我正在尝试使用泛型键入React组件的属性。问题是,当我试图通过另一个组件将该组件渲染为渲染道具时,它似乎不再能够“推断”正确的类型-相反,它默认为未知,我必须使用“as any”使其正常工作 我在这里列出了一个工作示例。请注意注释和“如有”: 代码如下: type TabProps<T> = { data?: T; }; type TabsProps<T> = { children: ({ childrenArr }: { children

我正在尝试使用泛型键入React组件的属性。问题是,当我试图通过另一个组件将该组件渲染为渲染道具时,它似乎不再能够“推断”正确的类型-相反,它默认为未知,我必须使用“as any”使其正常工作

我在这里列出了一个工作示例。请注意注释和“如有”:

代码如下:

type TabProps<T> = {
  data?: T;
};

type TabsProps<T> = {
  children: ({
    childrenArr
  }: {
    childrenArr: React.ReactElement<TabProps<T>>[];
  }) => React.ReactElement<TabProps<T>>[];
  items: React.ReactElement<TabProps<T>>[];
};

type Data = { hello: string };

const Tab = <T extends unknown>({ data }: TabProps<T>) => <div>...</div>;

const Tabs = <T extends unknown>({ children, items }: TabsProps<T>) => {
  const childrenArr = useMemo(() => (Array.isArray(items) ? items : [items]), [
    items
  ]);

  // ...
  // In real application this is where manipulation of the elements in the childrenArr would take place
  // Since this is just an example I'll just do this
  const manipulatedchildrenArr = childrenArr.map(child => {
    return {
      ...(child as any),
      props: { data: { hello: "world is manipulated" } }
    };
  });

  return (
    <div>
      <div>Hello</div>
      <div>
        {typeof children === "function"
          ? children({ childrenArr: manipulatedchildrenArr })
          : children}
      </div>
    </div>
  );
};

const App = () => {
  const data: Data = { hello: "World" };

  return (
    <div className="App">
      <Tabs items={[<Tab data={data} />]}>
        {({ childrenArr }) =>
          childrenArr.map(child => (
            // Remove "as any" and it will be type unknown and result in an error
            <div>{(child.props as any).data.hello}</div>
          ))
        }
      </Tabs>
    </div>
  );
};
type TabProps={
数据?:T;
};
类型TabsProps={
儿童:({
childrenArr
}: {
childrenArr:React.ReactElement[];
})=>React.ReactElement[];
项目:React.ReactElement[];
};
类型数据={hello:string};
常量Tab=({data}:TabProps)=>。。。;
常量选项卡=({children,items}:TabsProps)=>{
const childrenArr=useMoom(()=>(Array.isArray(items)?items:[items])[
项目
]);
// ...
//在实际应用中,这是在childrenArr中操作元素的地方
//因为这只是一个例子,我就这样做
const操纵的childrenArr=childrenArr.map(child=>{
返回{
…(任何儿童),
道具:{数据:{你好:“世界被操纵了”}
};
});
返回(
你好
{typeof children==“函数”
?儿童({childrenArr:manufacturedchildrenarr})
:children}
);
};
常量应用=()=>{
const data:data={hello:“World”};
返回(
{({childrenArr})=>
childrenArr.map(child=>(
//删除“as any”,则其类型未知并导致错误
{(child.props作为任何).data.hello}
))
}
);
};

正如您所看到的,
数据类型
道具丢失。

现在我不确定我是否超出了您所寻找的范围,如果超出了范围,请告诉我,我将调整解决方案

更新:我忘了为单个选项卡添加代码

import React from "react";
import ReactDOM from "react-dom";


export interface ITabProps<T> {
  data?: T;
  handleProcessData: (data: T) => string;
}

export function Tab<T>(props: ITabProps<T>) {
  const data = props.data ? props.handleProcessData(props.data) : "None";
  return <div>Hello {data}</div>;
}


export type TabElement<T> = React.ReactElement<ITabProps<T>> | React.ReactElement<ITabProps<T>>[]


export interface ITabsProps<T> {
  handleManipulation: (data: T) => T;
  children: TabElement<T>
}

export function Tabs<T>(props: ITabsProps<T>) {
  const array = [] as TabElement<T>[];
  if (Array.isArray(props.children))
    props.children.forEach((child) => {
      let mChild = <Tab<T> handleProcessData={child.props.handleProcessData} data={props.handleManipulation(child.props.data)}  /> as TabElement<T>;
      array.push(mChild)
    })
  else {
    let mChild = <Tab<T> handleProcessData={props.children.props.handleProcessData} data={props.handleManipulation(props.children.props.data)}  /> as TabElement<T>;
    array.push(mChild)
  }
    
  return <div>{array.map((item) => (item))}</div>;
}


export type Data = { hello: string };

export function App() {

  //B.C. it's generic you going to have to have some form of generic control functions
  const handleProcessData = (data: Data) => {
    //Here you have to specifiy how this specific data type is processed
    return data.hello;
  };

  const handleManipulation = (data: Data) => {
    //here you would have all your manipulation logic
    return { hello: data.hello + " is manipulated" };
  }

 //To Make this easier to use you could nest handleProcessData inside the Tabs component
  return (
    <div>
      <Tabs<Data> handleManipulation={handleManipulation}>
        <Tab<Data> handleProcessData={handleProcessData} data={{hello: "world1"}} />
        <Tab<Data> handleProcessData={handleProcessData} data={{hello: "world2"}} />
      </Tabs>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

从“React”导入React;
从“react dom”导入react dom;
导出接口ITabProps{
数据?:T;
handleProcessData:(数据:T)=>字符串;
}
导出功能选项卡(道具:ITabProps){
const data=props.data?props.handleProcessData(props.data):“无”;
返回Hello{data};
}
导出类型TabElement=React.ReactElement | React.ReactElement[]
导出接口ITabsProps{
手部操作:(数据:T)=>T;
子元素:TabElement
}
导出功能选项卡(道具:ITabsProps){
常量数组=[]作为TabElement[];
if(Array.isArray(props.children))
props.children.forEach((child)=>{
设mChild=as TabElement;
array.push(mChild)
})
否则{
设mChild=as TabElement;
array.push(mChild)
}
返回{array.map((item)=>(item))};
}
导出类型数据={hello:string};
导出函数App(){
//B.C.它是通用的,你必须有某种形式的通用控制函数
常量handleProcessData=(数据:数据)=>{
//这里您必须指定如何处理此特定数据类型
返回data.hello;
};
常量句柄操作=(数据:数据)=>{
//在这里,您将拥有所有的操作逻辑
返回{hello:data.hello+“被操纵”};
}
//为了使其更易于使用,您可以将handleProcessData嵌套在Tabs组件中
返回(
);
}
render(,document.getElementById(“根”));

我现在正在研究一个解决方案,但是,您可以添加更多关于此代码最终目标的详细信息?顺便说一句,这可能会对您有所帮助,@Nicholas_Jones cool。使用data prop的原因是,我有时需要使用其中的数据在从render props传回自定义选项卡标签后有条件地呈现该标签。这就是为什么我将其存储在组件的属性中。通过渲染道具发送的原因是,在这种情况下,将元素拆分为多个元素数组,如可见/下拉(用于溢出选项卡)等。期待看到您的解决方案!感谢您抽出时间提出此解决方案。确实学会了一些技巧。很抱歉,如果我不太清楚我想要实现什么,但实际上我将把子数组分成可见和溢出部分,但是我也将根据数据属性中的内容(如果有)以不同的方式呈现标签。因此,这与其说是改变数据属性,不如说是基于给定子元素/选项卡元素的数据对象中的内容对子元素进行更改。问题是,当试图使用渲染道具实现这一点时,数据会丢失其类型