如何为setState添加包装函数?

时间:2019-06-27 16:04:49

标签: javascript reactjs typescript

我在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})} />

因为我的更新功能实际上比我在此处粘贴的功能要多

3 个答案:

答案 0 :(得分:2)

您的尝试即将结束。您遇到的问题是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>);
}

答案 1 :(得分:0)

收到此错误的原因是,当您声明以下内容时:

let updatedField = {};

变量updatedField的类型为空对象。因此,当您尝试在其上设置属性时,TypeScript会告诉您您不能执行此操作,因为根据定义,空对象没有属性:在类型'上未找到参数类型为'string'的索引签名。 {}'

解决问题的一种简单方法是将您的代码更改为此:

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

这将立即创建一个具有动态"computed" property名称(您的字段)和事件传递的值的对象,然后将其提供给setState

编辑:正如Aaron Beall所说,TypeScript在此处显示错误,因为计算出的属性不是您所在州类型的一部分。你应该参考他们的答案

答案 2 :(得分:0)

您可以使用高阶函数来概括更新特定字段。这是一个示例实现:

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>