I am currently creating a Compound Component type component. At that time I am looking for a way to propagate generics when using render prop.
interface ListContextValue<T> {
rows: T[];
}
const createListContext = once(<T,>() =>
createContext<ListContextValue<T> | undefined>(undefined)
);
const useListContext = <T,>() => {
const context = useContext(createListContext<T>());
if (!context) {
throw new Error(
"useInfiniteList must be used within an InfiniteListProvider"
);
}
return context;
};
interface ListProps<T> {
rows: T[];
children?: React.ReactNode;
}
export const List = <T,>({ rows, children }: ListProps<T>) => {
const ListContext = createListContext<T>();
return (
<ListContext.Provider value={{ rows }}>
{children}
</ListContext.Provider>
);
};
export interface ListContainerProps<T> extends Omit<SlotProps, "children"> {
children?: (props: { row: T }) => React.ReactNode;
}
export const ListContainer = <T,>({ children }: ListContainerProps<T>) => {
const { rows } = useListContext<T>();
return (
<div>
{rows.map((row) => {
return children ? children({ row }) : null;
})}
</div>
);
};
Above is the List component code.
export default function UserList() {
return (
<List rows={rows}>
<ListContainer>
{({ row }) => (
<div>
{/** row is unknown type */}
{row}
</div>
)}
</ListContainer>
</List>
);
}
When using the component, is it possible to automatically infer the row type in the render prop function?
export default function UserList() {
return (
<List<User[]> rows={rows}>
<ListContainer<User>>
{({ row }) => (
<div>
{row}
</div>
)}
</ListContainer>
</List>
);
}
Of course, it can be solved this way, but I don't think it's a good way.