Entrada de mecanografía onchange event.target.value

142

En mi reaccionar y aplicación mecanografiado, yo uso: onChange={(e) => data.motto = (e.target as any).value}.

¿Cómo defino correctamente los tipings para la clase, para que no tenga que hackear el sistema de tipos any?

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

}

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

Si pongo target: { value: string };me sale:

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'.
ojos salvajes
fuente

Respuestas:

243

En general, los controladores de eventos deben usar e.currentTarget.value, por ejemplo:

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

Puede leer por qué aquí ( revierta "Make SyntheticEvent.target genérico, no SyntheticEvent.currentTarget" ).

UPD: Según lo mencionado por @ roger-gusmao, ChangeEventmás adecuado para escribir eventos de formulario.

onChange = (e: React.ChangeEvent<HTMLInputElement>)=> {
   const newValue = e.target.value;
}
Yozi
fuente
17
Esto simplemente no funciona. el valor no es una propiedad de la interfaz EventTarget
tocqueville
1
Por supuesto, no EventTarget, sino parte de HTMLInputElement. Puede ver la definición completa aquí github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/…
Yozi
1
Oh lo siento, solías currentTarget. En ese caso sí, funciona, pero la pregunta era sobretarget
tocqueville el
1
Sí, tienes razón, pero como se menciona en github.com/DefinitelyTyped/DefinitelyTyped/pull/12239 usando targetincorrecto en la mayoría de los casos. Además, el objetivo no tiene Tque obligarnos a escribir correctamente
Yozi
1
Esto no funcionó para mí, tuve que lanzar el evento en React.ChangeEvent<HTMLInputElement>lugar de un FormEvent.
Oblivionkey3
86

la forma correcta de usar en TypeScript es

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

Siga la clase completa, es mejor entender:

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
fuente
3
Nota: Para garantizar tipos están disponibles, añadir lib: ["dom"]a compilerOptionsentsconfig.json
James Conkling
1
@JamesConkling ¡Muchas gracias!
Alexandre Rivest el
1
Y si tiene múltiples entradas, ¿necesita hacer una fila para cada una?
Trevor Wood el
Otra forma de asegurarse de que 'this' se asigne adecuadamente en la función handleChange sería escribir handleChange como una función de flecha, es decir, handleChange = (e: React.ChangeEvent <HTMLInputElement>) => {this.setState (...); }; Al hacer eso, uno ya no tendría que usar el constructor para vincular la función handleEvent.
tlavarea
Una forma más de manejar 'esto' en lugar de usar el constructor y el método de enlace sería usar la función de flecha en el accesorio onChange, es decir, onChange = {e => this.handleChange (e)}
tlavarea
12

as HTMLInputElement funciona para mi

haind
fuente
9

Lo targetque intentaste agregar InputPropsno es lo mismo targetque querías, que está enReact.FormEvent

Entonces, la solución que se me ocurrió fue, extender los tipos relacionados con eventos para agregar su tipo de destino, como:

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

Una vez que tenga esas clases, puede usar su componente de entrada como

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

sin errores de compilación. De hecho, también puede usar las dos primeras interfaces anteriores para sus otros componentes.

Leona
fuente
¡El tipo de valor no es una cadena!
Roger Gusmao
7

Por suerte, encuentro una solución. usted puede

importar {ChangeEvent} desde 'reaccionar';

y luego escribe código como: e:ChangeEvent<HTMLInputElement>

Linshuizhaoying
fuente
2

Aquí hay una manera con la desestructuración de objetos ES6, probada con TS 3.3.
Este ejemplo es para una entrada de texto.

name: string = '';

private updateName({ target }: { target: HTMLInputElement }) {
    this.name = target.value;
}
poli
fuente
1

Esto es cuando estás trabajando con un FileListobjeto:

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
fuente
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
  }

Y asegúrese de tener "lib": ["dom"]en su tsconfig.

avalancha1
fuente
1

Cuando se usa Componente secundario Verificamos tipos como este.

Componente principal:

export default () => {

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

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

Componente hijo:

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

export Input:React.FC<Props> ({onChange}) => (
  <input type="tex" onChange={onChange} />
)
Código
fuente
0

Una alternativa que aún no se ha mencionado es escribir la función onChange en lugar de los accesorios que recibe. Usando React.ChangeEventHandler:

const stateChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
    console.log(event.target.value);
};
fjplaurr
fuente
-1

Gracias @haind

HTMLInputElementtrabajado para el campo de entrada

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

Esta HTMLInputElementinterfaz se hereda de la HTMLElementcual se hereda desde el EventTargetnivel raíz. Por lo tanto, podemos afirmar que usar el asoperador para usar interfaces específicas de acuerdo con el contexto, como en este caso estamos usando HTMLInputElementpara el campo de entrada, pueden ser otras interfaces HTMLButtonElement, HTMLImageElementetc.

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

Para obtener más información, puede consultar otra interfaz disponible aquí

Ali Jamal
fuente