React-jsonschema-form: Loses characters on Internet Explorer 11

Created on 13 Oct 2016  Â·  28Comments  Â·  Source: rjsf-team/react-jsonschema-form

Description

Letters of input show up and then rapidly disappear at random when typing quickly on IE 11.

Steps to Reproduce

When using Internet explorer 11, locally using modern.ie VM and interacting with the first text box on https://mozilla-services.github.io/react-jsonschema-form/ . A client initially ran into the issue running IE 11 on her desktop.

Quickly typed in: This is steven thompson

Expected behavior

Should have gotten This is steven thompson in the text box.

Actual behavior

screen shot 2016-10-12 at 4 17 14 pm

Version

Current

All 28 comments

Verified that it's a race condition getting data through the onChange propagators out to <Form /> then propagating everything back up through the chain. Profiling shows it's creating around 15k new React elements in my case - seems a little much. Going to move those out of the render functions and re-render as needed - holler if you'd like a different approach.

Same issue here in meteor and browsing with chrome.
I just realized that the entry point of the package was "lib/index.js" which was not optimized.
Using import Form from 'react-jsonschema-form/dist/react-jsonschema-form'; instead solve my issue.

which was not optimized.

What do you mean, not optimized?

@israelshirk is the problem occuring with liveValidate set to false (which should be the default)?

AFK, I'll check this afternoon. Thanks, Israel

On Oct 18, 2016, at 11:25 AM, Nicolas Perriault [email protected] wrote:

@israelshirk is the problem occuring with liveValidate set to false (which should be dthe default)?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

There were 2 build script according to package.json

"build:lib": "rimraf lib && NODE_ENV=production babel -d lib/ src/"

"build:dist": "rimraf dist && NODE_ENV=production webpack --config webpack.config.dist.js --optimize-minimize"

If I understand correctlly, the 'lib' one was just build ES6 into plain javascript and it's still human readable while the 'dist' one will bundle and optimize the code which brought a better performance.

However,

"main": "lib/index.js"

indecated that the entry point of the package was not the optimized one which was 'dist'.

@n1k0 The issue does seem worse with liveValidate turned on; I've been running into the issue with liveValidate = false.

There were 2 build script according to package.json

So the build:lib command builds the module files to be published for the npm package, as native es6 is not yet an option. The build:dist will indeed generate an optimized bundle, though that's not something you should import, really.

The typical supported workflows are:

  • Loading the lib as a <script> import in some webpage;
  • Importing the npm package module in your project code, then generate your own optimized bundle.

The issue does seem worse with liveValidate turned on

This is expected as liveValidate will validate the whole tree on each atomic change; we may want to move to atomic node validation in the future though.

For the initial issue, it seems that bidirectional event propagation between input and state gets in the way. We may want to consider buffering the event stream and chunking propagated change event data, but I'm not sure just yet. I'll think about it.

Hi,

I get the same issue with Firefox 50.1.0 . LiveValidate is turned off.

With knilink solution I resolved the problem, but n1k0 seems to discourage this.

If I try to set onChange on the component (which I didn't set before), with function that does nothing, the behaviour seems to be less frequent.

For an input form on production this behaviour is not really acceptable.

How to work around the problem ?

Thanks,

Maybe we need a way to do live validation only if nothing happen during the last 1 seconds because if the fields wait for the update before accepting any new characters it can cause this kind of problems.

Or maybe make it customizable

I tried noValidate property with the same result. Characters are loses when typing quickly. So perhaps the validation is not the cause of this issue.

So I updated node to v6. performed a total module re-installation. Isolated the Component and pass schema to via props. And I got the same behaviour. I have two form nested in another. I loses character in each form's first field. Moreover if I try my schema on playground it works perfectly... So the problem come surely from my code but I don't understand how. This my schema:


Click to expand

{
    "required": [
        "address",
        "client"
    ],
    "properties": {
        "address": {
            "properties": {
                "adresse": {
                    "type": "string",
                    "title": "Adresse"
                },
                "code_p": {
                    "type": "string",
                    "title": "Code postal"
                },
                "ville": {
                    "type": "string",
                    "title": "Ville"
                },
                "tel": {
                    "type": "string",
                    "title": "T\u00e9l\u00e9phone"
                }
            },
            "type": "object",
            "required": [
                "adresse",
                "code_p",
                "ville",
                "tel"
            ]
        },
        "client": {
            "properties": {
                "nom": {
                    "type": "string",
                    "description": ""
                },
                "code_externe": {
                    "type": "string",
                    "title": "code facturation"
                },
                "societe": {
                    "type": "string",
                    "title": "Soci\u00e9t\u00e9"
                },
                "email": {
                    "type": "string",
                    "title": "Email"
                },
                "siret": {
                    "type": "string",
                    "title": "SIRET"
                },
                "id_type_paiement": {
                    "enumNames": [
                        "Ch\u00e8que",
                        "Prelev",
                        "Virt"
                    ],
                    "type": "number",
                    "title": "Type paiement",
                    "enum": [
                        1,
                        2,
                        3
                    ]
                },
                "naf": {
                    "type": "number",
                    "title": "Code NAF"
                },
                "force": {
                    "type": "boolean",
                    "title": "force"
                }
            },
            "type": "object",
            "title": "Client",
            "required": [
                "nom",
                "societe",
                "email",
                "siret",
                "code_externe",
                "id_type_paiement"
            ]
        }
    },
    "description": "Cr\u00e9ation d'un client dans Admin2",
    "title": "Nouveau client",
    "type": "object"
}

I fetch this Schema with calling OPTIONS on url.


Click to expand

class CustomerSchemaFetcher extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            schema: null,
            error: null
        };
    }

    componentDidMount(props) {
        if (!this.state.schema)
            this.fetchOption()
    }

    fetchOption() {
        fetch(
            this.props.customer_url,
            {
                 credentials: 'same-origin',
                 method: 'OPTIONS'
             })
            .then(checkStatus)
            .then((response) => {
                const address = response.json()
                    .then((data) => {
                        this.setState({schema: data.methods.POST})})
            })
            .catch((error) => {
                this.setState({error: error.message});
                console.error(error);
            });
    }

    render(){
        if (this.state.schema)
            return ;
        else if (this.state.error)
            return ;
       else return null;
    }
}

class CustomerForm extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            error: null,
            data: {
                client: {
                    force: true}}};
        this.createCustomer = this.createCustomer.bind(this);
        this.change = this.change.bind(this);
    }

    change(form) {
        console.info("Change");
        console.log(form.formData);
    }

    createCustomer(form) {
        console.info("create new customer");
        console.log(form.formData);
        fetch(
            this.props.url,
            {
                 credentials: 'same-origin',
                 method: 'POST',
                 headers: {
                     'Content-Type': 'application/json',
                     'X-CSRFToken': getCookies("csrftoken")
                 },
                 body: JSON.stringify(form.formData)
             })
            .then(checkStatus)
            .then((response) => {
                const address = response.json()
                    .then((data) => {
                        this.props.update(data)})
            })
            .catch((error) => {
                console.error(error.response);
                error.response.json()
                    .then((data) => {
                        console.info('error is serializable');
                        console.log(data);
                        this.setState({
                            data: form.formData,
                            error: data.details.errors});
                    })
                    .catch((ex) => this.setState({
                            data: form.formData,
                            error: error.message}));
            });
    }

    render(){
        console.info("render with:");
        console.log(this.state.data);
        return (
            
                { this.state.error &&
                }
                
); } }

I put some logging outputs for understand but I have the same behaviour without.

In my case, another workaround was to return false in shouldComponentUpdate.
So I guess it would be great if uncontrolled formData is supported such as
<Form schema={...} onChange={...}/> without formData.
Or
<Form schema={...} defaultFormData={...} onChange={...}/>

I'm having the same problem with IE11, but interestingly, when I set it to emulate IE 10 everything is ok.

I am currently facing a similar issue with all my forms in all browsers. I can see multiple rendering of the input widget when I type quickly, but form onChange happen just after the multiple rendering. It definitely looks like a race condition. Continuing investigation as it's really a problem in my case (If I type "azeazeazeaze", I get "aaaaaaze" most of the time - in a form that have only 5 text fields). Pretty sure this can be related to custom widget performance somehow.

Pretty sure this can be related to custom widget performance somehow.

@MoOx dunno if it could be that, but there's a performance trick when it comes to passing function as props and bind them in the render function directly, then the functions are always treated as different, triggering rerenders.

A common trick is to bind them in the constructor, or to use this syntax:

class Foo extends Component {
  onChange = (event) => {
    this.whatever()
  }
  shouldComponentUpdate(nextProps) {
    return nextProps.onChange !== this.props.onChange;
  }
  render() {
    return <input onChange={this.onChange}/>;
  }
}

But it could be a million other things. I'm happy you're investigating this as it's been reported a few times already.

@MoOx side question, does what you describe also happen with the playground? because it's using the trick mentioned above to circumvent unecessary rerenders. I can't reproduce the missing input events everybody seems to experiment with the playground on my box.

Was not able to reproduce with online demos in https://mozilla-services.github.io/react-jsonschema-form/
I already use the arrow method class property syntax, so this is probably something. Stay tuned ;)

@MoOx now I think more about it, there's also this trick with setImmediate https://github.com/mozilla-services/react-jsonschema-form/blob/master/playground/app.js#L132-L137

There's a reusable setState helper too.

Why don't you

this.setState(
  {[name]: parseFloat(event.target.value)},
  () => this.props.onChange(this.state))
)

so you are sure this.state is up to date? This reminds me this https://github.com/mozilla-services/react-jsonschema-form/issues/315#issuecomment-256930949

Mostly because that would block the UI until the render is entirely finished, which would do exactly what we're trying to fight here... The safeRenderCompletion option of the setState helper does exactly this, which is useful in a test environment where you want to check things after they've been fully rendered.

TBH it's been a while I didn't touch this area of the code, feel free to (re)experiment with all this, i'm pretty sure I got something wrong.

480 may just have solved this; could anyone test latest master to see if it fixes the issue on their end? Thanks.

Going to try this today.

This fixes it for my IE11 VirtualMachine.

Ok closing as this seems to be gone now tanks to #480.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

elyobo picture elyobo  Â·  3Comments

abhishekpdubey picture abhishekpdubey  Â·  3Comments

j-zimnowoda picture j-zimnowoda  Â·  3Comments

FBurner picture FBurner  Â·  3Comments

jabaren picture jabaren  Â·  3Comments