226 votes

Valeur par défaut d'une propriété dans un composant React à l'aide de TypeScript

Je n'arrive pas à trouver comment définir des valeurs de propriété par défaut pour mes composants à l'aide de Typescript.

Voici le code source :

class PageState
{
}

export class PageProps
{
    foo: string = "bar";
}

export class PageComponent extends React.Component<PageProps, PageState>
{
    public render(): JSX.Element
    {
        return (
            <span>Hello, world</span>
        );
    }
}

Et quand j'essaie d'utiliser le composant comme ceci :

ReactDOM.render(<PageComponent />, document.getElementById("page"));

Je reçois une erreur disant que la propriété foo a disparu. Je veux utiliser la valeur par défaut. J'ai également essayé d'utiliser static defaultProps = ... à l'intérieur du composant, mais cela n'a eu aucun effet comme je le soupçonnais.

src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.

Comment puis-je utiliser les valeurs de propriété par défaut ? De nombreux composants JS utilisés par mon entreprise en dépendent et ne pas les utiliser n'est pas un choix.

0 votes

static defaultProps est correct. Pouvez-vous afficher ce code ?

420voto

Aaron Points 14679

Props par défaut avec la classe composant

Utilisation de static defaultProps est correcte. Vous devriez également utiliser des interfaces, et non des classes, pour les props et l'état.

Mise à jour 2018/12/1 : TypeScript a amélioré la vérification de type liée à defaultProps au fil du temps. Lisez ce qui suit pour connaître les dernières et meilleures utilisations, ainsi que les utilisations et problèmes plus anciens.

Pour TypeScript 3.0 et plus

TypeScript en particulier ajout d'un support pour defaultProps pour que la vérification du type fonctionne comme vous le souhaitez. Exemple :

interface PageProps {
  foo: string;
  bar: string;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, { this.props.foo.toUpperCase() }</span>
        );
    }
}

qui peut être rendu et compilé sans passer un foo attribut :

<PageComponent bar={ "hello" } />

Notez que :

  • foo est pas marqués comme facultatifs (c'est-à-dire foo?: string ) même s'il n'est pas requis en tant qu'attribut JSX. Le marquer comme facultatif signifierait qu'il pourrait être undefined mais en fait, elle ne le sera jamais. undefined parce que defaultProps fournit une valeur par défaut. Pensez-y de la même manière que [vous pouvez marquer un paramètre de fonction comme optionnel, ou avec une valeur par défaut, mais pas les deux, pourtant les deux signifient que l'appel n'a pas besoin de spécifier une valeur](http://www.typescriptlang.org/play/#src=function%20optionalParam(param%3F%3A%20string)%20%7B%20%7D%0D%0A%0D%0Afunction%20defaultParam(param%3A%20string%20%3D%20%22foo%22)%20%7B%20%7D%0D%0A%0D%0AoptionalParam()%3B%0D%0AdefaultParam()%3B) . TypeScript 3.0+ traite defaultProps de manière similaire, ce qui est vraiment cool pour les utilisateurs de React !
  • Le site defaultProps n'a pas d'annotation de type explicite. Son type est déduit et utilisé par le compilateur pour déterminer les attributs JSX requis. Vous pouvez utiliser defaultProps: Pick<PageProps, "foo"> pour garantir defaultProps correspond à un sous-ensemble de PageProps . Pour en savoir plus sur cette mise en garde, voir expliqué ici .
  • Cela nécessite @types/react version 16.4.11 pour fonctionner correctement.

Pour TypeScript 2.1 jusqu'à 3.0

Avant que TypeScript 3.0 n'implémente la prise en charge par le compilateur des éléments suivants defaultProps vous pouvez toujours l'utiliser, et il fonctionne à 100% avec React au moment de l'exécution, mais comme TypeScript ne prend en compte que les props lors de la vérification des attributs JSX, vous devez marquer les props qui ont des valeurs par défaut comme facultatives avec ? . Exemple :

interface PageProps {
    foo?: string;
    bar: number;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps: Partial<PageProps> = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, world</span>
        );
    }
}

Notez que :

  • C'est une bonne idée d'annoter defaultProps avec Partial<> de manière à ce qu'il vérifie le type de vos props, mais vous n'avez pas à fournir une valeur par défaut à chaque propriété requise, ce qui n'a aucun sens puisque les propriétés requises ne devraient jamais avoir besoin d'une valeur par défaut.
  • Lorsque vous utilisez strictNullChecks la valeur de this.props.foo sera possibly undefined et exigent une assertion non nulle (c'est-à-dire this.props.foo! ) ou d'une protection de type (c'est-à-dire if (this.props.foo) ... ) pour supprimer undefined . C'est ennuyeux car la valeur par défaut de prop signifie qu'elle ne sera jamais indéfinie, mais TS n'a pas compris ce flux. C'est l'une des principales raisons pour lesquelles TS 3.0 a ajouté le support explicite de defaultProps .

Avant TypeScript 2.1

Cela fonctionne de la même manière mais vous n'avez pas à Partial donc il suffit d'omettre Partial<> et soit fournir des valeurs par défaut pour tous les props requis (même si ces valeurs par défaut ne seront jamais utilisées), soit omettre complètement l'annotation de type explicite.

Props par défaut avec Composants fonctionnels

Vous pouvez utiliser defaultProps sur les composants de fonction également, mais vous devez taper votre fonction dans le champ FunctionComponent ( StatelessComponent sur @types/react avant la version 16.7.2 ) afin que TypeScript connaisse l'interface defaultProps sur la fonction :

interface PageProps {
  foo?: string;
  bar: number;
}

const PageComponent: FunctionComponent<PageProps> = (props) => {
  return (
    <span>Hello, {props.foo}, {props.bar}</span>
  );
};

PageComponent.defaultProps = {
  foo: "default"
};

Notez que vous n'êtes pas obligé d'utiliser Partial<PageProps> n'importe où parce que FunctionComponent.defaultProps est déjà spécifié comme un partiel dans TS 2.1+.

Une autre alternative intéressante (c'est celle que j'utilise) est de déstructurer votre props et d'attribuer directement des valeurs par défaut :

const PageComponent: FunctionComponent<PageProps> = ({foo = "default", bar}) => {
  return (
    <span>Hello, {foo}, {bar}</span>
  );
};

Alors vous n'avez pas besoin de la defaultProps du tout ! Sachez que si vous faire fournir defaultProps sur un composant de fonction, il aura la priorité sur les valeurs de paramètres par défaut, car React transmettra toujours explicitement l'attribut defaultProps (les paramètres ne sont donc jamais indéfinis, le paramètre par défaut n'est donc jamais utilisé). Il faut donc utiliser l'un ou l'autre, pas les deux.

0 votes

J'ai fait ctrl+c/ctr+v de votre code (et corrigé le point-virgule qui ne devrait pas être là) et il me donne la même erreur : src/typescript/main.tsx(8,17) : error TS2324 : La propriété 'foo' est absente du type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children ? ReactEle...'.

2 votes

L'erreur semble indiquer que vous utilisez <PageComponent> quelque part sans passer par la foo prop. Vous pouvez le rendre facultatif en utilisant foo?: string dans votre interface.

0 votes

Oui, exactement, car j'ai besoin qu'il utilise la valeur par défaut spécifiée dans defaultProps - lorsque je passe le prop à l'endroit où je l'utilise, il est écrasé, n'est-ce pas ?

23voto

Learner Points 577

Avec Typescript 2.1+, utilisez Partiel < T > au lieu de rendre les propriétés de votre interface facultatives.

export interface Props {
    obj: Model,
    a: boolean
    b: boolean
}

public static defaultProps: Partial<Props> = {
    a: true
};

19voto

AmerllicA Points 1

Composant fonctionnel

En fait, pour les composants fonctionnels, la meilleure pratique est la suivante, je crée un exemple de composant Spinner :

import React from 'react';
import { ActivityIndicator } from 'react-native';
import { colors } from 'helpers/theme';
import type { FC } from 'types';

interface SpinnerProps {
  color?: string;
  size?: 'small' | 'large' | 1 | 0;
  animating?: boolean;
  hidesWhenStopped?: boolean;
}

const Spinner: FC<SpinnerProps> = ({
  color,
  size,
  animating,
  hidesWhenStopped,
}) => (
  <ActivityIndicator
    color={color}
    size={size}
    animating={animating}
    hidesWhenStopped={hidesWhenStopped}
  />
);

Spinner.defaultProps = {
  animating: true,
  color: colors.primary,
  hidesWhenStopped: true,
  size: 'small',
};

export default Spinner;

11voto

Christopher Points 265

Vous pouvez utiliser l'opérateur d'étalement pour réaffecter les accessoires avec un composant fonctionnel standard. Ce que j'aime dans cette approche, c'est que vous pouvez mélanger des accessoires obligatoires avec des accessoires facultatifs qui ont une valeur par défaut.

interface MyProps {
   text: string;
   optionalText?: string;
}

const defaultProps = {
   optionalText = "foo";
}

const MyComponent = (props: MyProps) => {
   props = { ...defaultProps, ...props }
}

0 votes

À mon avis, c'est la solution la plus propre et la plus facile à lire.

0 votes

Il pourrait être préférable d'utiliser une variable temporaire pour fusionner au lieu d'écraser le paramètre. props

0 votes

@jfunk pourquoi ?????

10voto

CorayThan Points 2190

Avec Typescript 3.0, il existe un nouvelle solution à cette question :

export interface Props {
    name: string;
}

export class Greet extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return <div>Hello ${name.toUpperCase()}!</div>;
    }
    static defaultProps = { name: "world"};
}

// Type-checks! No type assertions needed!
let el = <Greet />

Notez que pour que cela fonctionne, vous devez disposer d'une version plus récente de l'application @types/react que 16.4.6 . Il fonctionne avec 16.4.11 .

0 votes

Excellente réponse ! Comment pourrait-on gérer : export interface Props { name?: string;} où le nom est un en option prop ? Je continue à obtenir TS2532 Object is possibly 'undefined'

0 votes

@Fydo Je n'ai pas eu besoin d'avoir une valeur par défaut pour une option, depuis que j'ai commencé à travailler sur le sujet. undefined est une sorte de valeur par défaut automatique pour ces accessoires. Vous voulez être capable de passer dans undefined comme valeur explicite parfois, mais avoir une autre valeur par défaut ? Avez-vous essayé export interface Props {name: string | undefined;} à la place ? Je n'ai pas essayé, c'est juste une idée.

0 votes

Ajout de ? est la même chose que d'ajouter |undefined . Je veux optionnellement passer dans le prop, et laisser defaultProps gérer ce cas. On dirait que ce n'est pas encore possible dans TS3. Je vais juste utiliser le redoutable name! syntaxe puisque je sais que ce n'est jamais undefined quand defaultProps sont réglés. Merci quand même !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X