Как добавить функцию-оболочку для setState?

У меня есть форма регистрации пользователя в 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})} />

потому что мои функции обновления на самом деле делают немного больше, чем я вставил здесь

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
416
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы получаете эту ошибку, потому что, когда вы объявляете это:

let updatedField = {};

переменная updatedField имеет тип пустого объекта. Таким образом, когда вы пытаетесь установить для него свойство, TypeScript сообщает вам, что вы не можете этого сделать, потому что пустой объект по определению не имеет свойства: Не найдена сигнатура индекса с параметром типа "строка" для типа "{}"

Простым способом решения проблемы было бы изменить код следующим образом:

updateField(
  evt: React.ChangeEvent<
    HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement
  >,
  field: string
) {
  this.setState({
    [field]: evt.target.value
  });
}

Это немедленно создает объект с динамическим именем «вычисленное» свойство (ваше поле) и значением, переданным событием, которое затем передается setState.

Редактировать: Как сказал Аарон Билл, TypeScript показывает здесь ошибку, потому что вычисляемое свойство не является частью типа вашего состояния. Вы должны обратиться к их ответу

setState не собирается принимать этот аргумент, однако
Aaron Beall 27.06.2019 18:47

Вы абсолютно правы, я не помнил, насколько строг TypeScript. Я должен был включить универсальный тип.

MrAnima 27.06.2019 18:59

Вы можете использовать функцию более высокого порядка для обобщения обновления определенных полей. Вот пример реализации:

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

Другие вопросы по теме