sta de typoscript-compiler toe om setState aan te roepen op slechts één react state-eigenschap

Ik gebruik Typescript met React voor een project. De hoofdcomponent krijgt een status met deze interface.

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

De onderstaande code (alleen een uittreksel) kan echter niet worden gecompileerd.

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
    });
  }
}

De compiler klaagt dat, in de onChange-methode, waarbij ik alleen State probeer in te stellen voor één eigenschap, de eigenschap todosen de eigenschap hungryontbreekt in type { editorState: EditorState;}. Met andere woorden, ik moet de status van alle drie de eigenschappen in de functie onChangeinstellen om de code te laten compileren. Om het te compileren, moet ik doen

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

maar er is geen reden om de eigenschap todosen de eigenschap hungryop dit punt in de code in te stellen. Wat is de juiste manier om setState op slechts één eigenschap aan te roepen in typoscript/react?


Antwoord 1, autoriteit 100%

Bewerken

De definities voor reageren zijn bijgewerkt en de handtekening voor setStateis nu:

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

Waar Pick<S, K>een ingebouwd type is dat is toegevoegd in Typescript 2.1:

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

Zie Toegewezen typenvoor meer informatie.< br>
Als je deze fout nog steeds hebt, kun je overwegen om je reactiedefinities bij te werken.

Oorspronkelijke antwoord:

Ik zit met hetzelfde.

De twee manieren waarop ik dit vervelende probleem kan omzeilen zijn:

(1) casting/bewering:

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

(2) de interfacevelden als optioneel declareren:

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

Als iemand een betere oplossing heeft, hoor ik het graag!


Bewerken

Hoewel dit nog steeds een probleem is, zijn er twee discussies over nieuwe functies die dit probleem zullen oplossen:
Gedeeltelijke typen (optionele eigenschappen voor bestaande typen)
en
Nauwkeuriger typen van Object.assign en React component setState()


Antwoord 2, autoriteit 36%

Status expliciet bijwerken, voorbeeld met de teller

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

Antwoord 3, autoriteit 14%

Ik denk dat de beste manier om dit te doen is om Partial

. te gebruiken

Declareer uw component op de volgende manier

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

Gedeeltelijk verandert automatisch alle toetsen in optioneel.


Antwoord 4, autoriteit 5%

We kunnen setStatevertellen welk veld het moet verwachten, door het te parametreren met <'editorState'>:

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

Als u meerdere velden bijwerkt, kunt u deze als volgt specificeren:

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

hoewel je er eigenlijk mee wegkomt door er maar één te specificeren!

Hoe dan ook, met de nieuwste TS en @types/reactzijn beide bovenstaande optioneel, maar ze leiden ons naar…


Als u een dynamische sleutelwilt gebruiken, kunnen we setStatelaten weten dat er geen velden in het bijzonder worden verwacht:

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

en zoals hierboven vermeld, klaagt het niet als we een extra veld doorgeven.

(GitHub-probleem)


Antwoord 5, autoriteit 2%

Bewerken: GEBRUIK deze oplossing NIET, liever https://stackoverflow.com/a/41828633/1420794
Zie opmerkingen voor details.

Nu de spread-operator in TS is verzonden, is mijn voorkeursoplossing

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

Antwoord 6, autoriteit 2%

Ik hou niet van casten en tijdelijke oplossingen die je veel ruimte geven om fouten te maken terwijl je je project opschaalt. Hier is mijn oplossing.

  1. We moeten de juiste intellisense voor de sleutel krijgen door een generiek type te gebruiken

<K extends keyof MainState>

  1. Vervolgens moeten we het juiste type voor waarde krijgen op basis van de ingevoerde sleutel

typeof MainState[K]

  1. Om code te krijgen zonder en een fout en zonder casting/workarounds enz. te gebruiken, geef je callback door aan this.setState

  2. Allemaal combineren


onChange = <K extends keyof MainState>(fieldName: K, value: typeof MainState[K]) => {
        this.setState((prevState) => {
            return {
                ...prevState,
                [fieldName]: value,
            };
        });
    };

Other episodes