Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear button on iOS date input does not return correct event value #8938

Open
ro-savage opened this issue Feb 7, 2017 · 20 comments
Open

Clear button on iOS date input does not return correct event value #8938

ro-savage opened this issue Feb 7, 2017 · 20 comments

Comments

@ro-savage
Copy link

ro-savage commented Feb 7, 2017

Bug
For iOS only.
When pressing clear on a date input, the onChange event is fired but event.target.value is showing the original value rather than an empty string.

What is the current behavior?
On Chrome and Android, when the clear button is pressed the onChange event has a value of ''.
On iOS when the clear button is pressed the onChange event has a value of previousValue.

Demo
https://output.jsbin.com/zojuteloto/5/
Try on Chrome/Android. Then on iOS.

What is the expected behavior?
Value should be returned an an empty string.

Versions
Affects React 15+ & iOS 10. Unsure of previous versions.

@nhunzaker
Copy link
Contributor

Purely speculative, but I wonder if clear is restoring the input to its defaultValue, which is stored under the value attribute on controlled fields, much like form.reset() (which React controlled inputs don't support).

Happy to triage this, @aweary.

@ro-savage
Copy link
Author

Updating to use defaultValue="" doesn't seem to have an effect.

http://output.jsbin.com/ceyelifiso/1

@aweary
Copy link
Contributor

aweary commented Feb 7, 2017

@nhunzaker feel free to look into this 👍 I did some very shallow/quick debugging and from what I can tell iOS Safari is not updating the DOM element's .value property when it's cleared. So nativeEvent.target.value is still the old value, as opposed to Chrome which resets it to ""

It would be worth seeing if we could reproduce this behavior without React.

@nhunzaker
Copy link
Contributor

Without React:
http://codepen.io/nhunzaker/pen/VPGPzw

Seems to pick it up no problem. iOS 10 7+ emulator:
ios-clear

@nhunzaker
Copy link
Contributor

I can reproduce this locally using the 15.4.2 build, but, curiously, it doesn't seem like the "clear" button even triggers a change event with the edge build on master:

ios-clear

(aside: it's awesome that this is super easy now 🌈 ⭐️)

@nhunzaker
Copy link
Contributor

@jquense no need to dig too deep into this, but the changes to ChangeEventPlugin jump out to me as the biggest difference between 15.x and 16.x. Any immediate thoughts?

@nhunzaker
Copy link
Contributor

nhunzaker commented Feb 8, 2017

Took a quick pass using the core mechanic behind inputValueTracking:
http://codepen.io/nhunzaker/pen/dNqvyM

Seems to work fine. I'll work my way out from there.

☝️ Sorry, bit of user error... Going to keep digging.

@mdoelker
Copy link

mdoelker commented Jun 7, 2017

Hitting Clear will indeed revert to the defaultValue DOM prop (representing the HTML attribute value) which works as expected for uncontrolled inputs. For controlled inputs, as React will update both value and defaultValue DOM props to the new value on every change, hitting Clear will have no noticeable effect. Setting the defaultValue DOM prop to the empty string manually after each change fixes this.

class DateInput extends React.Component {
  componentDidUpdate(prevProps) {
    if (prevProps.value !== this.props.value) {
      this.refs.input.defaultValue = "";
    }
  }

  render() {
    return <input ref="input" type="date" value={this.props.value} />;
  }
}

@mrj0
Copy link

mrj0 commented Aug 31, 2017

Tried the solution by @mdoelker but unfortunately didn't have any luck. The clear button still doesn't work but the defaultValue of the input element on the page is "".

@someclass
Copy link

@mdoelker's solution worked for me as long as I removed the check comparing prevProps.value to props.value (that is, on every componentDidUpdate, I manually set the input's default value to "").

@gaearon gaearon changed the title Bug: Clear button on iOS date input does not return correct event value Clear button on iOS date input does not return correct event value Oct 4, 2017
@arjunmehta
Copy link

I'm still encountering this. Tapping on clear does not clear the field, nor restore it to its defaultValue.

@waynebloss
Copy link

The workarounds listed so far didn't work for me. The following worked perfectly though on iOS 11.2.2, no ref needed -

onInput(e) {
    const target = e.nativeEvent.target;
    function iosClearDefault() { target.defaultValue = ''; }
    window.setTimeout(iosClearDefault, 0);
  }

@nhunzaker
Copy link
Contributor

We received a new issue for this (#12313) with some additional test cases. I've migrated over the issuer's findings to this issue:

Reproduced on iPhone 6 iOS 11.2.2 Safari.

Controlled Input type="date" doesn't trigger onChange by pressing the clear button on iOS Datepicker Keyboard. This only happens on controlled input, uncontrolled works fine.

Demo React 16.2: jsfiddle
Demo React 15.6.2 jsfiddle

Thanks @smollweide for your time preparing these!

@stereobooster
Copy link

stereobooster commented May 15, 2018

I was able to fix it with:

const getRef = (ref) => { if (ref) ref.defaultValue = "" };

//...

<input type="date" 
  onChange={(event) => {
    log('change controlled, value: ' + event.target.value);
    this.setState({ value: event.target.value });
  }} 
  value={this.state.value} 
  ref={getRef} />

Demo React 16.2 jsfiddle

@nhunzaker
Copy link
Contributor

That's very interesting. Both cases mentioned get me curious about this part:

input.defaultValue = ""

Is it possible that synchronizing the value attribute is messing up the value property reported by Safari when clearing the value?

I wonder if removing attribute syncing in a local build would alleviate the problem.

My guess is that there is a race between the browser and React, but I can't really reason about what that looks like. I'll dig into this a bit more.

@stereobooster
Copy link

stereobooster commented May 15, 2018

@nhunzaker When you click clear in Chrome it will reset value to empty string but Safari will reset value to defaultValue or to initial value. By setting defaultValue to empty string we force Safari to reset value to empty string. Issue can be reproduced without React (with HTML and vanilla JS), this is specifics of Safari.

@joseplatas
Copy link

Here is another solution to this issue in case someone needs it.

iosDateInput

https://codepen.io/joseplatas/pen/yWQYeq

class App extends React.Component{
  state = {
    value: "2011-11-11"
  }

  inputRef = React.createRef();
  
  componentDidMount(){
    /*
    necessary if value is being set in mount, 
    otherwise the clear won't work on the first click
    */
    this.inputRef.current.defaultValue = "";
  }

  handleOnChange = (event)=>{
    this.setState({
      value: event.target.value
    });
    
    /*
    defaultValue seems to reset every time on the handleChange call,
    we need to set defaultValue again as it is change.

    setTimeout is required for this to work, it needs a small delay
    otherwise defaultValue gets unset
    */
    const target = event.target; 
    setTimeout(()=>{
      target.defaultValue = "";
    }, 100);
  }
  
	render(){
    return(
      <div>
        <input 
          ref={this.inputRef}
          type="date" 
          value={this.state.value} 
          onChange={this.handleOnChange}
          /*
          NOTE: don't set defaultValue here because it will create a warning
          React will warn you that:
          Input elements must be either controlled or uncontrolled (specify               either the value prop, or the defaultValue prop, but not both).
          */
          //defaultValue=""
          />
      </div>
    )
  }
}

@aorcsik
Copy link

aorcsik commented Aug 7, 2019

I was experimenting with the solution of @joseplatas above for a while, but couldn't get the refs part working. Then I figured that making Clear work on first click can be done if I can clear defaultValue at the time of focusing the input, so attaching a callback to onFocus was all I needed.

return (
  <input
    type="date"
    value={this.state.value}
    onChange={this.handleOnChange}
    onFocus={(event) => event.nativeEvent.target.defaultValue = ""}
  />
);

@nhunzaker
Copy link
Contributor

nhunzaker commented Aug 28, 2019

Interesting. This looks like yet another side effect of assigning the value attribute dynamically, synchronizing it with value.

@philipp-spiess or @gaearon do you know of a place where we're tracking these issues? This issue might go away if we turn on the disableInputAttributeSyncing feature flag for everyone.

// React Fire: prevent the value and checked attributes from syncing
// with their related DOM properties
export const disableInputAttributeSyncing = false;

(EDIT: reading the comments, it looks like I'm re-remembering this for the second or third time 😭)

munen added a commit to 200ok-ch/organice that referenced this issue Dec 8, 2019
     Didn't work due to iOS bug.
     For more information: facebook/react#8938
@shriphad-rao
Copy link

Is this issue still open? I'm interested in working on it and would love to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests