У меня есть форма регистрации пользователя в React, которая обновляет состояние при изменении содержимого текстовых полей имени пользователя и пароля, например:
<TextField onChange = {e => this.updateUser(e)} />
updateUser(
evt: React.ChangeEvent<
HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement
>
) {
const user = evt.target.value;
this.setState({ user });
}
Однако я хотел бы использовать обобщить функции updateUser() и updatePassword(), чтобы при добавлении дополнительных полей в компонент я мог вызывать что-то вроде updateField(e, "password"), а не писать новую функцию для обновления каждого поля в состоянии.
Я пробовал это:
updateField(
evt: React.ChangeEvent<
HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement
>,
field: string
) {
let updatedField = {};
updatedField[field] = evt.target.value; // error here
this.setState(updatedField);
}
ошибка, которую я получаю,
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'
я этого не делаю
<TextField onChange = {e => this.setState({user:e.target.value})} />
потому что мои функции обновления на самом деле делают немного больше, чем я вставил здесь



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы получаете эту ошибку, потому что, когда вы объявляете это:
let updatedField = {};
переменная updatedField имеет тип пустого объекта. Таким образом, когда вы пытаетесь установить для него свойство, TypeScript сообщает вам, что вы не можете этого сделать, потому что пустой объект по определению не имеет свойства: Не найдена сигнатура индекса с параметром типа "строка" для типа "{}"
Простым способом решения проблемы было бы изменить код следующим образом:
updateField(
evt: React.ChangeEvent<
HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement
>,
field: string
) {
this.setState({
[field]: evt.target.value
});
}
Это немедленно создает объект с динамическим именем «вычисленное» свойство (ваше поле) и значением, переданным событием, которое затем передается setState.
Редактировать: Как сказал Аарон Билл, TypeScript показывает здесь ошибку, потому что вычисляемое свойство не является частью типа вашего состояния. Вы должны обратиться к их ответу
Вы абсолютно правы, я не помнил, насколько строг TypeScript. Я должен был включить универсальный тип.
Вы можете использовать функцию более высокого порядка для обобщения обновления определенных полей. Вот пример реализации:
class ExampleForm extends React.Component {
state = {
name: '',
password: ''
}
setStateFromValue = prop => ({target:{value}}) => {
this.setState({[prop]: value})
}
render () {
return (
<form>
<input onChange = {this.setStateFromValue('name')} />
<input type = "password" onChange = {this.setStateFromValue('password')} />
<div>
State: {JSON.stringify(this.state)}
</div>
</form>
)
}
}
ReactDOM.render(<ExampleForm />, document.querySelector("#form"))<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id = "form"></div>Ваша попытка близка. Ваша проблема заключается в том, что ключ поля string слишком широк, это может быть что угодно, поэтому средство проверки типов не позволит использовать его в качестве ключа состояния. Вы хотите, чтобы ключ был ключом вашего состояния. Предполагая, что вы находитесь в Component, где MyState — аргумент типа состояния, это должно сработать:
updateField<K extends keyof MyState>(
event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement>,
field: K
) {
const { value } = event.target;
this.setState({
[field]: value
} as Pick<MyProps, K>);
}
setStateне собирается принимать этот аргумент, однако