Wprowadzanie maszynopisu onchange event.target.value

141

W moim reagować i maszynopis aplikacji, używam: onChange={(e) => data.motto = (e.target as any).value}.

Jak poprawnie zdefiniować typy dla klasy, aby nie musieć hakować systemu typów any?

export interface InputProps extends React.HTMLProps<Input> {
...

}

export class Input extends React.Component<InputProps, {}> {
}

Jeśli wstawię target: { value: string };otrzymam:

ERROR in [default] /react-onsenui.d.ts:87:18
Interface 'InputProps' incorrectly extends interface 'HTMLProps<Input>'.
  Types of property 'target' are incompatible.
    Type '{ value: string; }' is not assignable to type 'string'.
dzikie oczy
źródło

Odpowiedzi:

243

Ogólnie rzecz biorąc, programy obsługi zdarzeń powinny używać e.currentTarget.valuenp .:

onChange = (e: React.FormEvent<HTMLInputElement>) => {
    const newValue = e.currentTarget.value;
}

Możesz przeczytać, dlaczego tak jest tutaj ( Przywróć „Make SyntheticEvent.target generic, a nie SyntheticEvent.currentTarget.” ).

UPD: Jak wspomniano w @ roger-gusmao, ChangeEventbardziej nadaje się do wpisywania zdarzeń formularza.

onChange = (e: React.ChangeEvent<HTMLInputElement>)=> {
   const newValue = e.target.value;
}
Yozi
źródło
17
To po prostu nie działa. value nie jest właściwością interfejsu EventTarget
tocqueville
1
Oczywiście nie EventTarget, ale część HTMLInputElement Pełną definicję można zobaczyć tutaj github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/…
Yozi
1
Przepraszam, użyłeś currentTarget. W takim razie tak, to działa, ale pytanie dotyczyłotarget
tocqueville
1
Tak, masz rację, ale jak wspomniano na github.com/DefinitelyTyped/DefinitelyTyped/pull/12239,target w większości przypadków używa się nieprawidłowego. Co więcej, cel nie musi Tnas zmuszać do poprawnego pisania
Yozi
1
To nie zadziałało w moim przypadku, musiałem React.ChangeEvent<HTMLInputElement>przesłać zdarzenie zamiast FormEvent.
Oblivionkey3,
86

prawidłowy sposób użycia w języku TypeScript to

  handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    // No longer need to cast to any - hooray for react!
    this.setState({temperature: e.target.value});
  }

  render() {
        ...
        <input value={temperature} onChange={this.handleChange} />
        ...
    );
  }

Postępuj zgodnie z całą klasą, lepiej zrozumieć:

import * as React from "react";

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};


interface TemperatureState {
   temperature: string;
}

interface TemperatureProps {
   scale: string;

}

class TemperatureInput extends React.Component<TemperatureProps, TemperatureState> {
  constructor(props: TemperatureProps) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  //  handleChange(e: { target: { value: string; }; }) {
  //    this.setState({temperature: e.target.value});  
  //  }


  handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    // No longer need to cast to any - hooray for react!
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature} onChange={this.handleChange} />
      </fieldset>
    );
  }
}

export default TemperatureInput;
Roger Gusmao
źródło
3
Uwaga: Aby zapewnić typy są dostępne, należy dodać lib: ["dom"]do compilerOptionswtsconfig.json
James Conkling
1
@JamesConkling Dziękuję bardzo!
Alexandre Rivest
1
A jeśli masz wiele danych wejściowych, czy musisz utworzyć wiersz dla każdego?
Trevor Wood,
Innym sposobem upewnienia się, że „this” jest odpowiednio przypisane w funkcji handleChange, byłoby zapisanie handleChange jako funkcji strzałkowej, tj. HandleChange = (e: React.ChangeEvent <HTMLInputElement>) => {this.setState (...); }; W ten sposób nie trzeba już używać konstruktora do wiązania funkcji handleEvent.
tlavarea
Innym sposobem obsługi „this” zamiast używania konstruktora i metody bind byłoby użycie funkcji strzałkowej we właściwości onChange, tj. OnChange = {e => this.handleChange (e)}
tlavarea
12

as HTMLInputElement pracuje dla mnie

haind
źródło
9

To, targetco próbujesz dodać, InputPropsnie jest tym samym target, co chceszReact.FormEvent

Tak więc rozwiązaniem, które mogłem wymyślić, było rozszerzenie typów związanych ze zdarzeniami, aby dodać typ celu, jako:

interface MyEventTarget extends EventTarget {
    value: string
}

interface MyFormEvent<T> extends React.FormEvent<T> {
    target: MyEventTarget
}

interface InputProps extends React.HTMLProps<Input> {
    onChange?: React.EventHandler<MyFormEvent<Input>>;
}

Gdy masz już te klasy, możesz użyć komponentu wejściowego jako

<Input onChange={e => alert(e.target.value)} />

bez błędów kompilacji. W rzeczywistości możesz również użyć dwóch pierwszych interfejsów powyżej dla innych komponentów.

Leone
źródło
Typ wartości nie jest ciągiem!
Roger Gusmao
7

na szczęście znajdę rozwiązanie. możesz

importuj {ChangeEvent} z 'reaguj';

a następnie napisz kod taki jak: e:ChangeEvent<HTMLInputElement>

Linshuizhaoying
źródło
2

Oto sposób z destrukturyzacją obiektów ES6, testowany z TS 3.3.
Ten przykład dotyczy wprowadzania tekstu.

name: string = '';

private updateName({ target }: { target: HTMLInputElement }) {
    this.name = target.value;
}
konstabl
źródło
1

Dzieje się tak, gdy pracujesz z obiektem FileList:

onChange={(event: React.ChangeEvent<HTMLInputElement>): void => {
  const fileListObj: FileList | null = event.target.files;
  if (Object.keys(fileListObj as Object).length > 3) {
    alert('Only three images pleaseeeee :)');
  } else {
    // Do something
  }

  return;
}}
Dryden Williams
źródło
1
  function handle_change(
    evt: React.ChangeEvent<HTMLInputElement>
  ): string {
    evt.persist(); // This is needed so you can actually get the currentTarget
    const inputValue = evt.currentTarget.value;

    return inputValue
  }

I upewnij się, że masz "lib": ["dom"]w swoim tsconfig.

lawina 1
źródło
1

Używając komponentu potomnego, sprawdzamy typ w ten sposób.

Komponent nadrzędny:

export default () => {

  const onChangeHandler = ((e: React.ChangeEvent<HTMLInputElement>): void => {
    console.log(e.currentTarget.value)
  }

  return (
    <div>
      <Input onChange={onChangeHandler} />
    </div>
  );
}

Komponent podrzędny:

type Props = {
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
}

export Input:React.FC<Props> ({onChange}) => (
  <input type="tex" onChange={onChange} />
)
Kod
źródło
0

Alternatywą, o której jeszcze nie wspomniano, jest wpisanie funkcji onChange zamiast otrzymywanych właściwości. Korzystanie z React.ChangeEventHandler:

const stateChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
    console.log(event.target.value);
};
fjplaurr
źródło
-1

Dzięki @haind

Tak HTMLInputElementpracował dla pola wprowadzania

//Example
var elem = e.currentTarget as HTMLInputElement;
elem.setAttribute('my-attribute','my value');
elem.value='5';

To HTMLInputElementjest interfejs jest dziedziczą z HTMLElementktórym jest dziedziczona z EventTargetna poziomie korzenia. Można więc stwierdzić, za pomocą asoperatora z określonych interfejsów w zależności od kontekstu, jak w tym przypadku jest używany HTMLInputElementdla pola wejściowego inne interfejsy mogą być HTMLButtonElement, HTMLImageElementetc.

https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement

Aby uzyskać więcej informacji, możesz sprawdzić inny dostępny interfejs tutaj

Ali Jamal
źródło