最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ReactJS: Save input value on blur and not on every key stroke - Stack Overflow

programmeradmin1浏览0评论

I have created a React View, say MyView, which has 2 text inputs whose initial values will be passed by parent read from a DB.

I also want the changed values to be saved back to DB. So, the view is also passed a callback function for the same.

Consider that DB save operation is heavy and you should not do it very frequently. So, I decided to listen to onBlur events instead of onChange events on the input boxes as onChange is invoked on every key stroke.

First Approach:

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>
        );
     }
}

However, this does not work as React enforces a controlled input (with value attribute) always to be acpanied by an onChange listener.

Second Approach:

So, I tried to make these inputs as uncontrolled. That is, instead of value attribute, used defaultValue.

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

But this also did not work as on reset/clear button click, although the view was made to re-render but defaultValue does not update once view is created.

Third Approach:

So, I finally added an onChange listener but as no-op.

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

Again, this did not work as the view re-renders after calling onChange and since value is not reflected in props yet, value seems to reset back to initial on every key stroke.

Fourth Approach:

Last I tried was to maintain a state in ponent and read value from state and on every onChange save the value back to state. This worked to most extent but whenever there were external changes to props and the view was re-rendered, state did not update. So, I added a getDerivedStateFromProps function to view:

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

Now, this again did not work. Reason being that this function is invoked even if I temporarily save values to state and the state was reset to initial values in props.

Can some ReactJS expert help me with my use-case?

I have created a React View, say MyView, which has 2 text inputs whose initial values will be passed by parent read from a DB.

I also want the changed values to be saved back to DB. So, the view is also passed a callback function for the same.

Consider that DB save operation is heavy and you should not do it very frequently. So, I decided to listen to onBlur events instead of onChange events on the input boxes as onChange is invoked on every key stroke.

First Approach:

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>
        );
     }
}

However, this does not work as React enforces a controlled input (with value attribute) always to be acpanied by an onChange listener.

Second Approach:

So, I tried to make these inputs as uncontrolled. That is, instead of value attribute, used defaultValue.

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

But this also did not work as on reset/clear button click, although the view was made to re-render but defaultValue does not update once view is created.

Third Approach:

So, I finally added an onChange listener but as no-op.

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

Again, this did not work as the view re-renders after calling onChange and since value is not reflected in props yet, value seems to reset back to initial on every key stroke.

Fourth Approach:

Last I tried was to maintain a state in ponent and read value from state and on every onChange save the value back to state. This worked to most extent but whenever there were external changes to props and the view was re-rendered, state did not update. So, I added a getDerivedStateFromProps function to view:

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

Now, this again did not work. Reason being that this function is invoked even if I temporarily save values to state and the state was reset to initial values in props.

Can some ReactJS expert help me with my use-case?

Share Improve this question edited Nov 26, 2018 at 11:19 Vaibhav Nigam asked Nov 26, 2018 at 10:55 Vaibhav NigamVaibhav Nigam 1,4871 gold badge14 silver badges23 bronze badges 2
  • As some first information, did the onChange work? Did it send the data to the database when it changed? – Craws Commented Nov 26, 2018 at 11:23
  • 1 @Craws If I use onChange instead of onBlur, yes, it totally works but I don't want to fire save on database on every key stroke. – Vaibhav Nigam Commented Nov 26, 2018 at 11:24
Add a ment  | 

3 Answers 3

Reset to default 4

You will still need onChange to help you set the states of both url input. onBlur is only used to trigger saving, it's 2 different events for different purposes.

Since your A & B values are passed down from parent ponent. MyView's parent ponent should pass down this.state.values and the functions to set the state.

Refer to this snippet if everything is in single ponent. You should be able move handleChange function up to its parent ponent.

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./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
</div>


EDIT: Your fourth approach should work with the following:

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

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

so basically the this.state.values is the final source of truth. When user types something, you setState in this ponent and change it. But if props.values changes (from external source), getDerivedStateFromProps will update the values state.

Going by the ments on Liren Yeo's solution, I would handle the props-state reconciliation on ponentDidUpdate, where you get both the old state and props. This way you can determine how this.props was updated and act accordingly. When the value in props does not match state nor oldProps, the update is external and you should override the unsaved changes in the state.

The code should look something like this

ponentDidUpdate(prevProps) {

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

If you go this route, you can also leave the input uncontrolled and update its value through a reference. This solves some unreliability with controlled inputs, like for example, a type='number' returning undefined as its value when you type a decimal ma. You still need to store the value onChange but only save it onBlur and handling the state-prop-dom reconciliation in ponentDidUpdate

So with the idea that onChange works, I would remend you to take a look at this:

https://schier.co/blog/2014/12/08/wait-for-user-to-stop-typing-using-javascript.html Navigate to the heading: Wait for Typing to Stop

Hope it can somehow lead you to what you want to achieve.

发布评论

评论列表(0)

  1. 暂无评论