【问题标题】:OnChange not saving the written input value on setStateOnChange 未在 setState 上保存写入的输入值
【发布时间】:2019-01-25 15:38:07
【问题描述】:

这似乎是一个愚蠢的问题,所以我提前道歉。

我有一个包含多个输入的页面,目前,出于某种原因,我不能只获取输入中写入的新值,因此我可以更新状态并发送这些新数据。在我看来,onChange 函数有问题,因为我可以获得以前的状态,但不能获得我在状态上保存的新值。

编辑: 提交按钮在输入表单之外。

代码如下:

constructor(props) {
    super(props)

    this.state = {
        editMode: false,
        data: {
            designation: '', 
            address: '', 
            description: ''
        }
    }
}

componentDidMount = () => {
    const dataInfo = data.get('url here');//fetching data here

    const data = {
        designation: dataInfo.designation , 
        address: dataInfo.address, 
        description: dataInfo.description 
    }

    this.setState({
        data: data
    })
}

handleInput = (e) => {
let value = e.target.value;
let name = e.target.name;

this.setState(
  prevState => ({
    data: {
      ...prevState.data,
      [name]: value
    }
  })
);
}

handleFormSubmit = (e) => {
    e.preventDefault();

    const { data } = this.state;

    console.log('hey', data.designation);

    this.setState({
        editMode: false
    })
 }

   render() {

   {!this.state.editMode 
? <button onClick={() => this.setState({ editMode: true })}>edit</button> 
: <div>
   <button 
   className={styles.buttonMargin} 
   onClick={() => this.setState({ editMode: false })}>cancel</button>
   <button onClick={this.handleFormSubmit}>Guardar</button>
  </div> 
    }

    <div>
      {this.state.editMode 
    ? <form onSubmit={this.handleFormSubmit}>
      <input 
        type='text'
        placeholder='Nome do Campo' 
        name='designation' 
        onChange={this.handleInput} 
        defaultValue={this.state.data.designation}
      />
      </form>
    : <p> {this.state.data.designation} </p> }
    </div>
 }
}

【问题讨论】:

  • 哪里没有最新的数据?在handleInput 方法还是render 方法中?
  • 你确定handleInput 函数被命中了吗?看来您需要将此函数绑定到范围。
  • 为什么 [name] 带有方括号?
  • function = () =&gt; {//code here} 这样的函数不需要this.function = this.function.bind(this)。 @BryanOfEarth
  • 我做了一个console.log(e.target.value) inside the handleInput(),我可以看到我在写什么。 @BryanOfEarth

标签: javascript reactjs


【解决方案1】:

我会推荐一些更改,我在这里有你的代码的工作版本:https://stackblitz.com/edit/react-vmeuxc

在构造函数中绑定函数将允许您稍后编写单元测试,并在需要时从组件外部访问函数。

根据我的经验,使用 e.currentTarget.value 比使用 e.target.value Difference between e.target and e.currentTarget 更稳定

我不明白您为什么将 this.state 分配给 handleFormSubmit 中的常量,所以我删除了它。

您缺少一些标记,例如表单上的提交按钮和未处于编辑模式时的编辑按钮。我也不明白你为什么在render 语句的末尾有随机的this.setState({ editMode: false }),所以我删除了它,因为它没有必要。

拥有这样的辅助函数将有助于检查新值的现有值。

  compareData(currentData, newData) {
    if(currentData === 'undefined' || (newData && currentData !== newData)) {
      return newData;
    } else {
      return false;
    }
  }

这是您的代码的完全清理版本。 注意:我必须创建一个虚拟的 dataInfo 对象,因为我无权访问您的 API。

  constructor(props) {
    super(props);
    this.compareData = this.compareData.bind(this);
    this.handleInput = this.handleInput.bind(this);
    this.handleFormSubmit = this.handleFormSubmit.bind(this);
    this.state = {
      editMode: false,
      data: {
        designation: '', 
        address: '', 
        description: ''
      }
    }
  }

  componentDidMount = () => {
    const dataInfo = {
      designation: 'test designation',
      address: 'test address',
      description: 'test description'
    };
    // const dataInfo = data.get('url here'); //fetching data here
    const data = {
      designation: dataInfo.designation , 
      address: dataInfo.address, 
      description: dataInfo.description 
    }

    this.setState({
        data: data
    })
  }

  compareData(currentData, newData) {
    if(currentData === 'undefined' || (newData && currentData !== newData)) {
      return newData;
    } else {
      return false;
    }
  }

  handleInput(e) {
    let value = e.currentTarget.value;
    let name = e.currentTarget.name;

    if(this.compareData(this.state.data[name], value)) {
      this.setState({
        data: {
          ...this.state.data,
          [name]: value
        }
      });
    }

    console.log(this.state);
  }

  handleFormSubmit(e) {
    e.preventDefault();

    this.setState({
      editMode: false
    });
  }

  render() {
    return (
      <div>
        {!this.state.editMode ? (
          <button onClick={() => this.setState({ editMode: true })}>edit</button> 
        ) : (
          <div>
            <button onClick={() => this.setState({ editMode: false })}>cancel</button>
            <button onClick={this.handleFormSubmit}>Guardar</button>
          </div> 
        )}

        {this.state.editMode ? (
          <form>
            <input 
              type='text'
              placeholder='Nome do Campo' 
              name='designation' 
              onChange={this.handleInput} 
              defaultValue={this.state.data.designation}
            />
          </form>
        ) : (
          <p> {this.state.data.designation} </p>
        )}
      </div>
    )
  }

【讨论】:

  • 现在我看到了您的代码,我在代码中添加了提交按钮。如果与您相关,该按钮位于表单之外。
  • @RCohen,在表单之外有一个表单提交按钮不是正确的标记,我什至认为这不会起作用。如果您想在表单之外有一个常规按钮并调用 handleFormSubmit 函数,那很好......但是只需从表单中删除您的 onSubmit={handleFormSubmit} ,因为它永远不会被调用
  • 好的,谢谢您的建议。但是,嘿,您的解决方案以某种方式与外面的按钮一起使用。那么,这里有什么问题呢?我只需要检查数据是否存在?@CWSites
  • 不客气,我在stackblitz.com/edit/react-vmeuxc 更新了我的代码,以便它与您的按钮所在的位置相匹配。我从我的样式中删除了样式,因为我无法访问您的样式。只要您调用handleFormSubmit 函数,该按钮就可以在任何地方工作。记住有效的 HTML 只是标记语义
【解决方案2】:

我稍微调整了您的代码。在我的情况下,虽然我建议您使用 value 而不是 defaultValue 来设置输入组件的值内容。我必须声明一个虚拟对象而不是获取数据,并将您的组件声明为标准 HTML 字段。我还添加了一个按钮来启用编辑模式,因为我不知道你在什么时候这样做。你可以在codesandbox io中测试它(链接到工作示例是https://codesandbox.io/s/n4lpz784wj):

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      editMode: false,
      data: {
        designation: '',
        address: '',
        description: ''
      }
    }
  }

  componentDidMount = () => {
    //const dataInfo = data.get('url here');//fetching data here
    const dataInfo = {
      designation: "Do something else",
      address: "5th Avenue #45",
      description: "This is a test description"
    }
    const data = {
      designation: dataInfo.designation,
      address: dataInfo.address,
      description: dataInfo.description
    }

    this.setState({
      data: data
    })
  }

  handleInput = (e) => {
    let value = e.target.value;
    let name = e.target.name;

    this.setState(
      prevState => ({
        data: {
          ...prevState.data,
          [name]: value
        }
      })
    );
  }

  toggleEditionMode = () => {
    this.setState({ editMode: true});
  }

  handleFormSubmit = (e) => {
    e.preventDefault();

    const { data } = this.state;

    console.log('hey', data.designation);

    this.setState({
      editMode: false
    })
  }

  render() {
    console.log(this.state.data.designation);
    return (
    <div>
      {this.state.editMode
        ? <form onSubmit={this.handleFormSubmit}>
          <input
            type='text'
            placeholder='Nome do Campo'
            name='designation'
            onChange={this.handleInput}
            defaultValue={this.state.data.designation}
          />

        </form>
          : <p> {this.state.data.designation} <button type="button" onClick={this.toggleEditionMode}>Enable edit</button> </p>}
    </div>
    );
  }

}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

【讨论】:

  • 您在此处具体更改了什么?除了虚拟数据。 @奥斯卡卡尔德隆
  • 现在我看到了代码,我在代码中添加了提交按钮。如果与您相关,该按钮位于表单之外。
  • @RCohen 主要是语法错误。在 render() 中,您没有 return 语句,并且在渲染结束时还有 this.setState (永远不要在渲染内部更新状态,这将导致渲染循环,渲染仅用于渲染组件而不是mutating state),也是您的示例中不存在的启用编辑模式的逻辑
  • @RCohen 关于在外面有提交按钮,这与问题无关。在按钮的事件处理程序中,您只需读取状态,这并不关心您的 html 控件是否位于同一表单中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
  • 2022-08-16
  • 2012-04-23
相关资源
最近更新 更多