newforms icon indicating copy to clipboard operation
newforms copied to clipboard

No re-render when changing field choices

Open Ismael opened this issue 10 years ago • 6 comments

In the following example, setting the ChoiceField's choices does not cause a redraw If I force a redraw, then I see the form correctly displayed.

I expected that setChoices would do a redraw on it's own.

var React = require("react");
var forms = require("newforms");
var IPIN = forms.Form.extend({
    device: forms.ChoiceField(),
});
var IPINform = new IPIN();

var Thing = React.createClass({
  getInitialState: function(){
    return {form: IPINform};
  },
  forceupdate: function(){
    this.forceUpdate();
  },
  render: function(){
    return (
            <form>
                <forms.RenderForm form={this.state.form} ref="ipin" />
                <button>submit</button>
            </form>
        );
  }
});

var r = React.render(<Thing/>, document.body);

setTimeout(function(){
  IPINform.fields.device.setChoices(["eth0", "eth1"]);
// If I add this, it works:
//  r.forceupdate();
}, 100);

Ismael avatar Mar 09 '15 17:03 Ismael

A Form knows how to re-render when its state changes, but a Field currently doesn't.

Will need to provide a way to enable this automatically, but you will have to force it to update manually in the meantime.

insin avatar Mar 09 '15 17:03 insin

Related: When validating the form, it doesn't re-render either.

Ismael avatar Mar 12 '15 16:03 Ismael

Looking at your example a bit closer, a couple of suggestions:

  • Redefining forceUpdate might actually be overriding the built-in one, so get rid of that.
  • You're not passing your form instance an onChange so it can trigger a re-render when it needs to.
  • If you're creating a Form instance yourself, do it inside the React component itself,like so:
  getInitialState: function(){
    return {
      form: new IPIN({onChange: this.forceUpdate.bind(this)})
    };
  },
  • An alternative to creating a form instance yourself is to let RenderForm do it for you, by passing a Form constructor. It will also handle setting up the form instance it creates with an onChange:

    <forms.RenderForm form={IPIN} ref="ipin" />
    

    You can get the Form instance later like so:

    this.refs.ipin.getForm()
    

insin avatar Mar 12 '15 16:03 insin

Redefining forceUpdate might actually be overriding the built-in one, so get rid of that.

This was just a simplified version. My code didn't use that function name.

You're not passing your form instance an onChange so it can trigger a re-render when it needs to.

Uh, it seems I skipped an entire section of the docs. My bad!

Changing the code as you suggested ( forms.RenderForm form={IPIN} ) seems to work correctly, both changing the choices and showing the validation.

Ismael avatar Mar 12 '15 16:03 Ismael

If I understood correctly, this should work, but it doesn't. I need to forceUpdate to see it.

var React = require("react");
var forms = require("newforms");

var IPIN = forms.Form.extend({
    device: forms.ChoiceField(),
});

var Thing = React.createClass({
  setthings: function(){
    this.refs["ipin"].getForm().fields.device.setChoices([1,2,3,4]);
  },
  render: function(){
    return (
            <form>
                <forms.RenderForm form={IPIN} ref="ipin" />
                <button>submit</button>
            </form>
        );
  }
});
var r = React.render(<Thing/>, document.body);

r.setthings();

Ismael avatar Mar 12 '15 17:03 Ismael

You will still need a forceUpdate() to re-render on choice changes until this issue is resolved.

insin avatar Mar 12 '15 21:03 insin