Vyhledávání na webu

javascript - Je možné vytvořit generickou komponentu seznamu v Reactu s TypeScriptem?


Vytvářím komponentu seznamu, která by měla zpracovávat různé objekty do seznamu. Ale nemohu najít řešení pro vykreslení položky. Seznam musí být generický, protože mám mnoho různých věcí k vypsání, ale možná neexistuje způsob a potřebuji vytvořit různé komponenty seznamu, které zpracují každý objekt?

Dostanu tuto chybu:

Property 'name' does not exist on type 'string | PrivilegeConditionOperator | PrivilegeConditionElement | Privilege'.
  Property 'name' does not exist on type 'string'.

Toto je kód:

const GetItem = (
    item: string | PrivilegeConditionOperator | PrivilegeConditionElement | Privilege,
    index: number
) => {
    switch (typeof item) {
        case typeof PrivilegeConditionElement:
            return `PrivilegeContionElement ${index}`;
        case typeof Privilege:
            return `Privilege ${index}`;
        case typeof PrivilegeCondition:
            return `PrivilegeContion ${index}`;
        case typeof PrivilegeConditionOperator:
            return item.name; //<===============ERROR when doing this
        default:
            return item;
    }
};

const List: FC<Props> = ({ handelRemove, itemsList }) => (
    <ListWrapper>
        <ul style={{ listStyleType: "none", padding: "0", margin: 0 }}>
            {itemsList.map((item, index) => (
                <DivWithToolTip
                    data-tooltip={item}
                    // eslint-disable-next-line react/no-array-index-key
                    key={index}
                    style={{
                        display: "flex",
                        justifyContent: "row",
                        alignContent: "center",
                        alignItems: "center",
                    }}>
                    <li style={{ marginTop: "10px" }}>{GetItem(item, index)}</li>
                    <PlusButton
                        style={{ marginTop: "10px" }}
                        {...{ addIcon: false }}
                        onClick={() => handelRemove(item)}
                    />
                </DivWithToolTip>
            ))}
        </ul>
    </ListWrapper>
);

interface IListProps {
    handelRemove: (item: string | PrivilegeConditionOperator | PrivilegeConditionElement | Privilege) => void;
    itemsList: Array<string | PrivilegeConditionOperator | PrivilegeConditionElement | Privilege>;
}
type Props = IListProps;

export default List;

Také jsem to zkoušel, ale stejná chyba:

<li style={{ marginTop: "10px" }}>{item.name && item}</li>

Odpověď č. 1

Generické komponenty na pomoc. Když pochopíte generický způsob, jak toho dosáhnout, můžete skutečně zjednodušit a vytvořit mnohem univerzálnější komponentu.

Problém zde je, že předáváte spoustu různých typů, ale vaše funkce getItem je pevně zakódovaná. Místo toho byste měli předat funkci getItem, která je vhodná pro typ, který se zobrazuje.

Řekněme, že definujete svůj rozhraní vlastností takto:

interface IListProps<T> {
    handleRemove: (item: T) => void;
    itemsList: T[];
    getItem: (v: T, index: number) => string; //pass this function into your component
}

a vaše komponenta jako:

const List = <T,>({ handleRemove, itemsList, getItem }: React.PropsWithChildren<IListProps<T>>): JSX.Element => (
    <ListWrapper>
        <ul style={{ listStyleType: 'none', padding: '0', margin: 0 }}>
            {itemsList.map((item, index) => (
                <DivWithToolTip
                    data-tooltip={item}
                    // eslint-disable-next-line react/no-array-index-key
                    key={index}
                    style={{
                        display: 'flex',
                        justifyContent: 'row',
                        alignContent: 'center',
                        alignItems: 'center',
                    }}
                >
                    <li style={{ marginTop: '10px' }}>{getItem(item, index)}</li>
                    <PlusButton
                        style={{ marginTop: '10px' }}
                        {...{ addIcon: false }}
                        onClick={() => handleRemove(item)}
                    />
                </DivWithToolTip>
            ))}
        </ul>
    </ListWrapper>
);

Nyní jej můžete použít s jakýmkoli typem položky a pomocí selektorové funkce getItem z ní získat správný řetězec.

const [items,setItems] = React.useState([{name:"Christian"}]);
return <List 
           itemsList = {items} 
           handleRemove = {(item) => setItems(items.filter(v => v != item)} 
           getItem={(v, i) => `${v.name}`} />

Odpověď č. 2

To, co máte, není problém specifický pro React, ale spíše pro TypeScript, protože kompilátor TypeScriptu si stěžuje na něco ve funkci GetItem, což není specifické pro React.

Předpokládáme-li, že PrivilegeConditionOperator je třída, nikoli interface nebo type, můžete místo switch používat příkazy if ... else if ... a místo typeof použít instanceof takto:

const GetItem = (
    item: string | PrivilegeConditionOperator | PrivilegeConditionElement | Privilege,
    index: number
) => {
        if (item instanceof PrivilegeConditionElement)
            return `PrivilegeContionElement ${index}`;
        else if (item instanceof Privilege)
            return `Privilege ${index}`;
        else if (item instanceof PrivilegeCondition)
            return `PrivilegeContion ${index}`;
        else if (item instanceof PrivilegeConditionOperator)
            return item.name;
        else
            return item;
    
};

Související články