Art der generischen zustandslosen Komponentenreaktion? ODER Erweiterung der generischen Funktionsschnittstelle in Typoskript um eine weitere generische?

Lesezeit: 8 Minuten

Benutzeravatar von Naman Kheterpal
Naman Kheterpal

Problem: Die Schnittstelle von Stateless Functional Component ist gegeben als

interface SFC<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
    propTypes?: ValidationMap<P>;
}

Der Prop-Typ meiner Komponente ist auch generisch als:

interface Prop<V>{
    num: V;
}

Wie definiere ich meine Komponente richtig? wie:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

gibt einen Fehler an character 27 das Cannot find name 'T'

Hier ist :Typescript Playground des modifizierten Beispiels

MeineErgebnisse:

1: Typescript 2.9.1 unterstützt Stateful Generic Component: http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements

class myCom<T> extends React.Component<Prop<T>, any> {
   render() {
      return <div>test</div>;
   }
}

2: Verlängerung SFC Um eine neue Schnittstelle zu erstellen, wie in der folgenden Antwort erwähnt, würde der Prop-Typ der Komponente wie folgt aussehen any: Typescript React zustandslose Funktion mit generischen Parameter-/Rückgabetypen, die ich nicht möchte. Ich möchte den richtigen Typ für meine Requisite angeben

  • Was passiert, wenn Sie das Generikum ersetzen <T> mit <T extends {}> ?

    – Jonas Wilms

    21. Juli 2018 um 20:57 Uhr

  • gleicher Fehler. Da ich so etwas möchte:const myCom: <T>SFC<Prop<T>>

    – Naman Kheterpal

    21. Juli 2018 um 21:00 Uhr

  • Ich habe den Playground-Link in der Beschreibung @JonasW hinzugefügt.

    – Naman Kheterpal

    21. Juli 2018 um 21:03 Uhr


  • @NamanKheterpal Ich bin auf dasselbe Problem gestoßen und habe ein bisschen Zeit dafür gesucht. Hier finden Sie meine Lösung, wie Sie einen React SFC deklarieren und verwenden

    – ford04

    4. November 2018 um 16:35 Uhr

Sie können keine Generika wie folgt verwenden:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

Die TypeScript-Spezifikation besagt:

Ein Konstrukt der Form

< T > ( ... ) => { ... }

könnte als Pfeilfunktionsausdruck mit einem Typparameter oder einer Typzusicherung analysiert werden, die auf eine Pfeilfunktion ohne Typparameter angewendet wird.

Quelle; Microsoft/TypeScript spec.md

Ihre Deklaration stimmt nicht mit dem in der TypeScript-Spezifikation definierten Muster überein, daher funktioniert sie nicht.

Sie können die SFC-Schnittstelle jedoch nicht verwenden und sie einfach selbst deklarieren.

interface Prop<V> {
    num: V;
}

// normal function
function Abc<T extends string | number>(props: Prop<T>): React.ReactElement<Prop<T>> {
    return <div />;
}

// const lambda function
const Abc: <T extends string | number>(p: Prop<T>) => React.ReactElement<Prop<T>> = (props) => {
   return <div />
};

export default function App() {
    return (
        <React.Fragment>
            <Abc<number> num={1} />
            <Abc<string> num="abc" />
            <Abc<string> num={1} /> // string expected but was number
        </React.Fragment>
    );
}

  • Kann dieses Muster irgendwie verwendet werden, wenn Sie einen Schnittpunkttyp mit haben React.FC? Zum Beispiel würde ich meine Komponente in der Lage sein, Typargumente zu akzeptieren, aber auch zusammengesetzte Komponenten zu haben. Derzeit habe ich eine const Lambda fn-Komponente const Table = React.FC<TableProps> & { Row: React.ComponentType<RowProps> } Und ich möchte in der Lage sein, einen Typparameter zu verwenden, wenn ich diese Komponente verwende.

    – Kizivat

    28. Mai 2020 um 20:48 Uhr


Es gibt ein Muster, um dieses Problem zu mindern, indem ein generischer Komponententyp-Alias ​​außerhalb der Komponente deklariert und dann einfach behauptet wird, wenn Sie ihn brauchen.

Nicht so hübsch, aber immer noch wiederverwendbar und streng.

interface IMyComponentProps<T> {
  name: string
  type: T
}

// instead of inline with component assignment
type MyComponentI<T = any> = React.FC<IMyComponentProps<T>>

const MyComponent: MyComponentI = props => <p {...props}>Hello</p>

const TypedComponent = MyComponent as MyComponentI<number>

  • Dies dient wahrscheinlich nicht dem Zweck von Generika. Der Entwickler muss möglicherweise die Funktionalität jeder Komponente neu schreiben und kann kein gemeinsames Verhalten haben. const MyComponent: MyComponentI = props => <p {...props}>Hello</p> Diese Zeile würde für jede Komponente wiederholt werden.

    – Mahesh

    18. März 2020 um 10:01 Uhr


Benutzeravatar von Chris
Chris

Fabrikmuster:

import React, { SFC } from 'react';

export interface GridProps<T = unknown> {
  data: T[];
  renderItem: (props: { item: T }) => React.ReactChild;
}

export const GridFactory = <T extends any>(): SFC<GridProps<T>> => () => {
  return (
    <div>
      ...
    </div>
  );
};

const Grid = GridFactory<string>();

UPDATE 03.08.2021

Um Hook-Regelfehler zu vermeiden, müssen Sie die Syntax wie folgt verfeinern:

import React, { FC } from 'react';

export interface GridProps<T = unknown> {
  data: T[]
  renderItem: (props: { item: T }) => React.ReactChild
}

export const GridFactory = <T extends any>() => {
  const Instance: FC<GridProps<T>> = (props) => {
    const [state, setState] = useState(props.data)

    return <div>...</div>
  }

  return Instance
}

const Grid = GridFactory<string>()

  • Ist <T extends any> verwendet, um zu vermeiden, dass TS es als Typzusicherung behandelt? Kluge Idee.

    – Bruce Sonne

    9. Februar 2020 um 3:17 Uhr

  • das ist richtig. Es kann hier auch einen anderen Typ erweitern, wenn dies erforderlich ist

    – Chris

    3. August 2021 um 21:40 Uhr

  • Ich verstehe es nicht … was würde es hier bedeuten, “es als Typbehauptung zu behandeln”? Würden Sie es bitte ein wenig erklären oder ein Beispiel dafür geben, was ohne das passiert extends any? Ich google darüber, finde aber nicht die richtige Richtung. Vielen Dank! @BruceSun & @chris

    – Rocío García Luque

    23. Mai um 7:03 Uhr


  • @RocíoGarcíaLuque Entschuldigung, ich habe mich geirrt, um zu vermeiden, dass der TS-Compiler es als JSX behandelt, aber nicht als “Type Assertion”. Kasse diese Codesandbox und seine Readme-Datei, um dies in einem echten Beispiel zu sehen.

    – Bruce Sonne

    30. Mai um 2:36 Uhr


  • @BruceSun ahhhh so eine dumme Sache: D Vielen Dank für die Codesandbox, ich hatte Mühe, dieses Codebit zu verstehen … Prost

    – Rocío García Luque

    30. Mai um 7:50 Uhr

Ich schlage eine ähnliche, wenn auch etwas andere Lösung vor (Brainstorming mit einem Freund). Wir haben versucht, einen Formik-Wrapper zu erstellen, und haben es auf folgende Weise geschafft, ihn zum Laufen zu bringen:

import React, { memo } from 'react';

export type FormDefaultProps<T> = {
  initialValues: T;
  onSubmit<T>(values: T, actions: FormikActions<T>): void;
  validationSchema?: object;
};

// We extract React.PropsWithChildren from React.FunctionComponent or React.FC
function component<T>(props: React.PropsWithChildren<FormDefaultProps<T>>) {
  // Do whatever you want with the props.
  return(<div>{props.children}</div>
}

// the casting here is key. You can use as typeof component to 
// create the typing automatically with the generic included..
export const FormDefault = memo(component) as typeof component;

Und dann verwenden Sie es wie folgt:

 <FormDefault<PlanningCreateValues>
        onSubmit={handleSubmit}
        initialValues={PlanningCreateDefaultValues}
      >
         {/*Or any other child content in here */}
        {pages[page]}
</FormDefault>

Ich konnte dies nicht mit Methodenausdrücken erreichen:

const a: React.FC<MyProp> = (prop) => (<>MyComponent</>);

Das hier von @chris vorgestellte Factory-Muster ist großartig, aber ich kann keine React Hooks damit verwenden. Also benutze ich diesen.

// Props
interface Props<T> {
  a: T;
}

// Component
export const MyComponent: <T>(p: PropsWithChildren<Props<T>>) => React.ReactElement = props => {
  return <div>Hello Typescript</div>;
};

Wenn Sie keine Kinder brauchen, können Sie den PropsWithChildren-Teil entfernen. Requisitenzerlegung und Hooks funktionieren ebenfalls.

export const MyComponent: <T>(p: Props<T>) => React.ReactElement = ({ a }) => {
  const [myState, setMyState] = useState(false);
  return <div>Hello Typescript</div>;
};

Ich habe einen Weg zu tun, aber ich bin mir nicht sicher, ob es perfekt ist oder nicht

interface ComponentProps<T> {
    text: T;
}

export const Component= <T,>(props: ComponentProps<T>) => {
  const { text } = props
  const [s] = useState(0) // you can use hook
  return (<div>yes</div>)
}

Sie können Komponenten wie folgt verwenden:

 (
  <>
    <Component<string> text="some text" />
  </>
 )

Benutzeravatar von Tuukka Haapasalo
Tuukka Haapasalo

Wenn Sie den FunctionComponent-Typ von React in die Typdefinition aufnehmen möchten (um beispielsweise den Rückgabetyp automatisch typgeprüft zu bekommen), können Sie ein Konstrukt wie dieses verwenden, wenn es Ihnen nichts ausmacht, die Typen ein wenig zu wiederholen:

interface MyProps<T> {
  val: T
}

const Component: React.FC<MyProps<any>> = <T>({
  val
}: MyProps<T>) => {
  // Your code and hooks here
  return <div>Heya</div>
}

Der Typ der Konstante Component ist React.FC<MyProps<any>>was bedeutet, dass Sie es mit jedem gültigen Wert für verwenden können MyProps Typparameter Taber da die Implementierung den generischen Typparameter neu angibt T innerhalb des Implementierungscodes ist sein Typ T und nicht anyund wird somit ordnungsgemäß typgeprüft. T könnte einschränkende Spezifizierer wie haben extends stringSie müssen sie nur sowohl in der Schnittstellenspezifikation als auch in der Signatur der Funktionsimplementierung wiederholen.

1403700cookie-checkArt der generischen zustandslosen Komponentenreaktion? ODER Erweiterung der generischen Funktionsschnittstelle in Typoskript um eine weitere generische?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy