4

我创建了一个 React View,比如说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. 也就是说,value使用 代替属性defaultValue

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

但这也不起作用,因为单击重置/清除按钮,虽然视图被重新渲染,但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 专家可以帮助我解决我的用例吗?

4

3 回答 3

4

您仍然需要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将更新值状态。

于 2018-11-26T11:24:31.277 回答
1

根据对 Liren Yeo 解决方案的评论,我componentDidUpdatestate处理props. 通过这种方式,您可以确定如何this.props更新并采取相应措施。当 in 的值props不匹配statenoroldProps时,更新是外部的,您应该覆盖状态中未保存的更改。

代码应该看起来像这样

componentDidUpdate(prevProps) {

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

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

于 2018-11-26T19:19:49.060 回答
0

因此,考虑到 onChange 有效,我建议您看一下:

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

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

于 2018-11-26T11:27:10.503 回答