web-dev-qa-db-fra.com

Autorise le compilateur TypeScript à appeler setState sur une seule propriété d'état de réaction

J'utilise TypeScript avec React pour un projet. Le composant principal est transmis à l'état avec cette interface.

interface MainState {
  todos: Todo[];
  hungry: Boolean;
  editorState: EditorState;  //this is from Facebook's draft js
}

Cependant, le code ci-dessous (seulement un extrait) ne sera pas compilé.

class Main extends React.Component<MainProps, MainState> {
  constructor(props) {
    super(props);
    this.state = { todos: [], hungry: true, editorState: EditorState.createEmpty() };
  }
  onChange(editorState: EditorState) {
    this.setState({
      editorState: editorState
    });
  }
}

Le compilateur se plaint du fait que, dans la méthode onChange où je ne tente de définir que l'état d'une propriété, la propriété todos et la propriété hungry sont manquantes dans le type { editorState: EditorState;}. En d'autres termes, je dois définir l'état des trois propriétés dans la fonction onChange pour que le code soit compilé. Pour compiler, je dois faire

onChange(editorState: EditorState){
  this.setState({
    todos: [],
    hungry: false,
    editorState: editorState
  });
}

mais il n'y a aucune raison de définir les propriétés todos et hungry à ce stade du code. Quelle est la bonne façon d'appeler setState sur une seule propriété dans TypeScript/react?

43
Leahcim

Modifier

Les définitions de react ont été mises à jour et la signature de setState est maintenant:

setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;

Pick<S, K> est un type intégré qui a été ajouté dans TypeScript 2.1:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}

Voir Types mappés pour plus d'informations.
Si vous rencontrez toujours cette erreur, vous pouvez envisager de mettre à jour vos définitions de réaction.

Réponse originale:

Je suis confronté à la même chose.

Les deux manières dont je parviens à contourner ce problème agaçant sont les suivantes:

(1) casting/assertion:

this.setState({
    editorState: editorState
} as MainState);

(2) déclarer les champs d'interface comme optionnels:

interface MainState {
    todos?: Todo[];
    hungry?: Boolean;
    editorState?: EditorState;
}

Si quelqu'un a une meilleure solution, je serais heureux de le savoir!


Modifier

Bien que cela reste un problème, deux discussions sont en cours sur les nouvelles fonctionnalités qui résoudront ce problème:
Types partiels (propriétés facultatives pour les types existants)
et
typage plus précis de Object.assign et React composant setState ()

51
Nitzan Tomer

Etat de mise à jour explicite, exemple avec le compteur

this.setState((current) => ({ ...current, counter: current.counter + 1 }))
18
Alexey Petrushin

Je pense que la meilleure façon de le faire est d'utiliser Partial

Déclarez votre composant de la manière suivante

class Main extends React.Component<MainProps, Partial<MainState>> {
}

Partial modifie automatiquement toutes les clés en option.

4
niba

Edit: NE PAS UTILISER cette solution, préférez https://stackoverflow.com/a/41828633/1420794 Voir les commentaires pour plus de détails.

Maintenant que cet opérateur de spread a été expédié dans TS, ma solution préférée est

this.setState({...this.state, editorState}); // do not use !
2
mlorber

On peut dire à setState à quel champ il devrait s’attendre, en le paramétrant avec <'editorState'>:

this.setState<'editorState'>({
  editorState: editorState
});

Si vous mettez à jour plusieurs champs, vous pouvez les spécifier comme ceci:

this.setState<'editorState' | 'hungry'>({
  editorState: editorState,
  hungry: true,
});

bien que cela vous permette de ne spécifier qu’un seul d’entre eux!

Quoi qu'il en soit avec le dernier TS et @types/react Les deux options ci-dessus sont facultatives, mais elles nous amènent à ...


Si vous souhaitez utiliser une clé dynamique , vous pouvez indiquer à setState de ne pas attendre de champs en particulier:

this.setState<never>({
  [key]: value,
});

et comme mentionné ci-dessus, il ne se plaint pas si nous passons un champ supplémentaire.

( numéro de GitHub )

0
joeytwiddle