ReactJS:在模糊而不是每次击键时保存输入值

ReactJS: Save input value on blur and not on every key stroke

我创建了一个 React 视图,比如 MyView,它有 2 个文本输入,其初始值将由父级从数据库读取传递。

我还想将更改后的值保存回数据库。因此,视图也传递了一个回调函数。

考虑到数据库保存操作繁重,不宜频繁进行。因此,我决定在输入框上监听 onBlur 事件而不是 onChange 事件,因为每次击键都会调用 onChange

第一种方法:

class MyView extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div>
              <input type="url" value={this.props.values.A}
                   onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
              <input type="url" value={this.props.values.B}
                   onBlur={(evt)=>{this.props.saveValue('B', evt.target.value)}} />         

              <button type="button" onClick={this.props.resetValues}>Reset</button>
            </div>
        );
     }
}

但是,这不起作用,因为 React 强制执行受控输入(具有 value 属性)始终伴随着 onChange 侦听器。

第二种方法:

因此,我尝试将这些输入设为 uncontrolled。也就是说,使用 defaultValue.

而不是 value 属性
<input type="url" defaultValue={this.props.values.A}
       onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />

但这也没有像 reset/clear 按钮单击时那样工作,虽然视图被重新渲染但是 defaultValue 一旦创建视图就不会更新。

第三种方法:

所以,我终于添加了一个 onChange 侦听器,但没有操作。

<input type="url" value={this.props.values.A}
       onChange={()=>{console.log('do nothing')}
       onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />

同样,这不起作用,因为调用 onChange 后视图重新呈现,并且由于值未反映在 props 中,value 似乎重置回初始值每一次击键。

第四种方法:

我最后尝试的是在组件中维护一个 state 并从状态读取值,并在每个 onChange 上将值保存回状态。这在很大程度上是有效的,但是只要道具有外部变化并且视图被重新渲染,state 就不会更新。所以,我加了一个getDerivedStateFromProps函数来查看:

static getDerivedStateFromProps(props, state) {
    return props.values;
}

现在,这又没用了。原因是即使我暂时将值保存到状态并且状态被重置为道具中的初始值,也会调用此函数。

一些 ReactJS 专家可以帮助我处理我的用例吗?

您仍然需要 onChange 来帮助您设置两个 url 输入的状态。 onBlur 仅用于触发保存,它是用于不同目的的 2 个不同事件。

因为您的 A 和 B 值是从父组件传递下来的。 MyView 的父组件应该向下传递 this.state.values 和设置状态的函数。

如果所有内容都在单个组件中,请参阅此代码段。您应该能够将 handleChange 函数移动到其父组件。

class App extends React.Component {

  state = {
    values: {
      A: '',
      B: ''
    }
  }

  handleChange = e => {
    this.setState({
      values: {
      ...this.state.values,
      [e.target.name]: e.target.value
    })
  }

  handleBlur = e => {
    if (e.target.name === 'A') {
      alert(`Saving A: ${this.state.values.A}`)
    }

    if (e.target.name === 'B') {
      alert(`Saving B: ${this.state.values.B}`)
    }
  }

  render() {
    return (
      <div>
        <label>Value A</label>
        <input
          type="url"
          name="A"
          value={this.state.values.B}
          onChange={this.handleChange}
          onBlur={this.handleBlur}
        />
        <label>Value B</label>
        <input
          type="url"
          name="B"
          value={this.state.values.A}
          onChange={this.handleChange}
          onBlur={this.handleBlur}
        />
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
</div>


编辑:您的第四种方法应该适用于以下内容:

static getDerivedStateFromProps(props, state) {
  return { values: props.values }
}

constructor(props) {
  super(props)
  this.state = {
    values: props.values
  }
}

所以基本上 this.state.values 是真相的最终来源。当用户键入内容时,您 setState 在此组件中进行更改。但是,如果 props.values 发生变化(来自外部源),getDerivedStateFromProps 将更新值状态。

所以有了 onChange 的想法,我建议你看看这个:

https://schier.co/blog/2014/12/08/wait-for-user-to-stop-typing-using-javascript.html 导航至标题:等待键入停止

希望它能以某种方式引导您实现您想要的目标。

根据对 Liren Yeo 解决方案的评论,我会在 componentDidUpdate 上处理 props-state 协调,在那里你可以得到旧的 stateprops。这样您就可以确定 this.props 是如何更新的并采取相应的行动。当 props 中的值既不匹配 state 也不匹配 oldProps 时,更新是外部的,您应该覆盖状态中未保存的更改。

代码应如下所示

componentDidUpdate(prevProps) {

if (this.props.values !== prevProps.values && this.props.values !== this.state.values) {
  this.setState({values:this.props.values});
   }
}

如果你走这条路,你也可以保留输入 uncontrolled 并通过引用更新它的值。这解决了 controlled 输入的一些不可靠性,例如,当您键入十进制逗号时,type='number' 返回 undefined 作为其值。您仍然需要存储值 onChange,但只需将其保存 onBlur 并在 componentDidUpdate

中处理 state-prop-dom 协调