reactjs - React TS generisk komponent til at sende generiske props til børn
Jeg vil skabe en React-komponent, der på en måde ligner et Akkordeon. Den vil have børn og tillade åbning/lukning af hver enkelt. Hvert barn er også en React-komponent, der har brug for unikke props fra forælderen, som andre børn måske ikke bruger. Jeg antog, at jeg skulle bruge Generics for at muliggøre disse forskellige props.
Jeg har dette CodeSandbox-projekt med mit forsøg.
Svar #1
For at sende komponenter som en prop, brug ComponentType i stedet for FC
type SubSectionToggleProps<T> = {
subSections: ComponentType<SubSectionBaseProps>[];
} & T;
Du kan være nødt til at type-assert de indsendte komponenter
<SubSectionToggle<{ title: string; count: number }>
title="Hello"
count={2}
subSections={[Component1, Component2] as ComponentType<SubSectionBaseProps>[]}
/>
CodeSandbox
Svar #2
En måde, hvorpå du kan gengive barnets isOpen-props, når de styres af containeren, er sådan her:
import React from 'react';
import "./styles.css";
// the container is your list
interface ContainerProps {
}
interface ContainerState {
// keep an array of isOpen variables in the state of the container
open: boolean[];
}
class Container extends React.Component<React.PropsWithChildren<ContainerProps>, ContainerState> {
public constructor(props: React.PropsWithChildren<ContainerProps>) {
super(props);
// initialize the isOpens
this.state = {
open: React.Children.toArray(this.props.children).map((_) => {return false;}),
};
}
public readonly render = () => {
// clone the children and specify their isOpen props here
return (
<>
{React.Children.map(this.props.children, (child, i) => {
if(React.isValidElement(child)) {
return(React.cloneElement(child, {isOpen: this.state.open[i]}));
}
return child;
})}
</>
);
};
}
// props for all list items to extend. The isOpen is allowed to be undefined for when the component is not in a list
interface ComponentProps {
isOpen?: boolean;
}
// component A uses a number
interface ComponentAProps extends ComponentProps {
count: number;
}
class ComponentA extends React.Component<ComponentAProps> {
public readonly render = () => {
return (
<p>
{`count: ${this.props.count}, open: ${this.props.isOpen}`}
</p>
);
};
}
// component B uses a string
interface ComponentBProps extends ComponentProps {
title: string;
}
class ComponentB extends React.Component<ComponentBProps> {
public readonly render = () => {
return (
<p>
{`title: ${this.props.title}, open: ${this.props.isOpen}`}
</p>
);
};
}
export default function App() {
return (
<Container>
<ComponentA count={2}/>
<ComponentB title='hello'/>
</Container>
);
}
Du gør isOpen-egenskaben valgfri. På den måde kan listekomponenten give isOpen til børnene, mens specifikationen af børnene kan ignorere isOpen-egenskaben.
