【问题标题】:React - < Input> value is not getting updated on onChange eventReact - < Input> 值未在 onChange 事件上更新
【发布时间】:2020-01-30 10:41:43
【问题描述】:

我有一个 Modal,其中包含动态构建的控制器(InputDropDownTextarea、...)。

我正在尝试实现Input 控制器onChange 方法的行为,以便在用户在其中输入内容后更新输入中的值。 (我尝试使用简单的 html input 和其他一些库 Input,例如 ant design - 但行为相同)。

为此,我为每个输入控制器指定了一个名称,并将事件发送到onChange,然后使用setState 设置一个状态变量,其中包含控制器名称及其值:

else if (element.Type === TEXT) {
  const inputName = element.Name.toLowerCase().replace(" ", "");
  this.setState({ [inputName]: element.Value }, () =>
    elements.push(
      <td className="view-cell" align="left">
        {element.Name}<br />
        <Input key={element.Id}
          name={inputName}
          style={{ width: '100%' }}
          // label={element.Name}
          defaultValue={element.Value}
          value={this.state[inputName]}
          disabled={!element.Editable}
          // size={element.Width}
          onChange={(e) => {
            this.onInputValueChange(e)
            this.onInputChange(element.Id, element.Name, e.target.value)
          }}
        />
      </td>
    ))
}


onInputValueChange(e) {
  const name = e.target.name
  this.setState({
    [e.target.name]: e.target.value
  }, () => {
    console.log(this.state[name]);
  });
}

问题是更改没有反映在屏幕上。 可能是什么原因? setState 是异步操作吗?

要重现,您可以使用此sandbox(在文本框中输入内容)

【问题讨论】:

标签: reactjs


【解决方案1】:

问题是您正在渲染您的组件处于某种状态。 你有你的输入,呈现在一个数组中,包裹在 html 周围,设置为一个状态。当您编辑某些输入值时,您正在更新您的状态,而不是输入数组。此数组中的某些值会发生变化,但包含此数组的状态不会发生变化 -> 此状态不会重新渲染,并且不会显示更改。

解决此问题的简单方法是通过删除 value={this.state[inputName]} 将输入从 controled 转换为 uncontroled 字段。您将能够使用this.state[inputName] 获取值,但您不再告诉输入它应该包含什么内容(这意味着您不能,例如,为电话号码输入+33 1 00 00 00 00 解析这样的数据,而不是默认+33100000000

困难的方法是通过编辑您的RuleEditor.js 来在每次更改时重新呈现您的数组状态(效率不高):

else if (element.Type === TEXT) {
  const inputName = element.Name.toLowerCase().replace(" ", "");

  /*
  ** NO -> You don't want to set the state for each render -> infinite loop risk
  */
  //this.setState({ [inputName]: element.Value })

  elements.push(
    <td className="view-cell" align="left">
    {element.Name}
    <br />
    <Input
      key={element.Id}
      name={inputName}
      style={{ width: "100%" }}
      value={this.state[inputName]}
      disabled={!element.Editable}
      onChange={e => {
        this.onInputValueChange(e);
    }} />
  </td>
);
  onInputValueChange(e) {
    const name = e.target.name;
    this.setState(
      {
        [name]: e.target.value
      },
      () => {
        //RE-SETSTATE FOR YOUR HEADER AND MAIN
        this.setState({
          header: this.buildRowsUsing_antD(this.ruleProperties.Parts.Header.Rows),
          main: this.buildRowsUsing_antD(this.ruleProperties.Parts.Main.Rows)
        });
      }
    );
  }

为此,将不使用 defaultValue。如果要设置默认值,则必须在呈现状态之前或之后(而不是在呈现状态时)将this.state[inputName] 分配给默认值。虽然不是那么好但简单的方法是做value={this.state[inputName] || element.Value}

【讨论】:

    【解决方案2】:
        onInputValueChange(e, index, i) {
          const name = e.target.name;
          let header = this.ruleProperties.Parts.Header.Rows
          header[i].Elements[index].Value = e.target.value
          console.log(header[i], header[i].Elements[index], "valuessssssss");
          this.setState(
            {
              header: this.buildRowsUsing_antD(header),
            },
            () => {
              console.log(header, "dfsaf");
            }
          );
        }
    
        buildRowsUsing_antD(rows) {
          let localRows = [];
          let elements = [];
          console.log("ressosss", rows);
          rows.map((row, i) => {
            elements = [];
            row.Elements.map((element, index) => {
              // console.log(element.Type);
              if (element.Type === LIST) {
                elements.push(
                  <td className="view-cell" align="left">
                    {element.Name}
                    <br />
                    <TreeSelect
                      // className=""
                      style={{ width: "100%" }}
                      // value={element.Value}
                      defaultValue={element.Value}
                      dropdownStyle={{
                        maxHeight: 400,
                        overflow: "auto",
                        zIndex: "10000",
                        position: "absolute"
                      }}
                      treeData={this.buildTreeData_antD(element.Values)}
                      // placeholder={element.Name}
                      // treeDefaultExpandAll
                      // onChange={this.onInputChange(element.Id, element.Name)}
                      onChange={val =>
                        this.onInputChange(element.Id, element.Name, val)
                      }
                    />
                  </td>
                );
              } else if (element.Type === "Text") {
                //   console.log(".......", element.Value);
                // -----------------------------------------------------------------------------------
                let inputName = element.Name.toLowerCase().replace(" ", "");
                // this.setState({ [inputName]: element.Value });
                //   this.setState({ [inputName]: element.Value }, () =>
                elements.push(
                  <td className="view-cell" align="left">
                    {element.Name}
                    <br />
                    <Input
                      key={element.Id}
                      name={inputName}
                      style={{ width: "100%" }}
                      // className="rounded"
                      // label={element.Name}
                      // defaultValue={element.Value}
                      value={element.Value}//do changes hereeeeeeeeee
                      disabled={!element.Editable}
                      // size={element.Width}
                      onChange={e => {
                        this.onInputValueChange(e, index, i);//pass indexxxxxxxx
                        // this.onInputChange(element.Id, element.Name, e.target.value)
                      }}
                    />
                  </td>
                );
                //   );
              } else if (element.Type === TEXTAREA) {
                const inputName = element.Name.toLowerCase().replace(" ", "");
                this.setState({ [inputName]: element.Value });
                //   this.setState({ [inputName]: element.Value }, () =>
                elements.push(
                  <td className="view-cell-full" width="100%">
                    {element.Name}
                    <br />
                    <TextArea
                      key={element.Id}
                      name={inputName}
                      // className="rounded"
                      componentclass="textarea"
                      rows={5}
                      style={{ width: "100%", resize: "auto" }}
                      // placeholder=""
                      disabled={!element.Editable}
                      defaultValue={element.Value}
                      value={this.state[inputName]}
                      onChange={e => {
                        this.onInputValueChange(e); // this.setState({inputVal: e.target.value})
                        // this.onInputChange(element.Id, element.Name, e.target.value)
                        //TODO - Assuming that text area is only for constraint field
                        //   this.props.ruleChangeCallBack(null, e.target.value);
                      }}
                    />
                  </td>
                );
                //   );
              } else if (element.Type === BUTTON) {
                elements.push(
                  <Button
                    key={element.Id}
                    // onClick={this.onInputChange(element.Id, element.Name, null)}
                    onClick={() => this.onInputChange(element.Id)}
                  >
                    {element.Value}
                  </Button>
                );
              } else if (element.Type === LABEL) {
                elements.push(
                  <td className="view-cell-full" align="left">
                    {element.Value}
                  </td>
                );
              }
            });
            // localRows.push(<div className="view-row">{elements}</div>)
            localRows.push(<tr className="view-row">{elements}</tr>);
          });
    
          return (
            <table width="100%" className="view-table">
              <tbody>{localRows}</tbody>
            </table>
          );
        }
    

    【讨论】:

    • 检查我所做的更改,在大-----行之后,您需要通过传递输入的索引 onChange 来更新用于渲染整个 JSX 的主数组
    • 非常感谢。我已经接受了类似的解决方案,非常感谢您的努力
    猜你喜欢
    • 1970-01-01
    • 2021-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 2013-10-26
    • 2022-06-28
    相关资源
    最近更新 更多