1

I have started writing a simple UI in React, and re-rendering input fields after an ajax call works fine on all components (text, select), except radio groups and checkboxes.

I can't for the life of me get checkboxes or radio groups to be correctly checked on a re-render after the "value" prop has been changed on a parent. I know that the component is getting re-rendered from console output, and I also now that the checked/selected state is being calculated correctly. However this is never reflected in the UI, which remains in the state it was on the initial render.

Code is below:

//Mixin used for all form inputs        
var InputField = {
        isValid : function(){
            var input = this.state.value;
            var valid = true;
            if(this.props.validations !== undefined){
                for(var i = 0; i < this.props.validations.length;i++){
                    if(valid){
                        valid = this.props.validations[i](input);
                    }
                }
            }

            this.setState({isValid: valid});
            return valid;
        },

        componentWillReceiveProps: function(newProps){
            this.setState({value: newProps.value});
        },

        getInitialState: function() {
          return {value: this.props.value, isValid: true};
        }
    };
    //the actual component:
    var RadioButtons = React.createClass({
        mixins: [InputField],
        handleChange: function(){
            var props = this.props;
            var selectedOpts = _.map($(React.findDOMNode(this.refs.radiobuttons)).find(':checked'), function(opt){ return parseInt(opt.value); });
            var values = _.map(selectedOpts, function(index){
                return props.options[index];
            });
            if(values.length > 0)
                this.setState({value: values[0]});
            else
                this.setState({value: null});
        },

        render: function(){
            var showProp = this.props.show;
            var i = 0;
            var self = this;
            var options =  _.map(self.props.options,function(opt){
                var selected = show(self.state.value,showProp) === show(opt,showProp);
                console.log(selected);
                var result = (<label className="radio">
                        <span className="checked"><input type="radio" name={self.props.name} value={i} checked={selected} onChange={self.handleChange}>{show(opt,showProp)}</input></span>
                    </label>);
                i = i + 1;
                return result;
            });

            return ( <FormField label={this.props.label} fieldClass="col-md-8" ref="radiobuttons" errorMessage={this.props.errorMessage} isValid={this.state.isValid}> 
            {options}
                </FormField> 
                   );
        }

    }); 

I am somewhat baffled by this, and would appreciate any help..

edit It seems every DOM element and value is set correctly, the only thing that is not happening is it being reflected visually. Suspect this might be a general browser/JS issue rather than specifically React?

wfaler
  • 166
  • 8

2 Answers2

1

I can't for the life of me get checkboxes or radio groups to be correctly checked on a re-render after the "value" prop has been changed on a parent

That's because getInitialState is only called for the first render. And that's why you should avoid passing the initial state as a prop. Here's an answer of mine regarding the same topic.

If you want future renders to reflect the new props being passed in, do not store their values in the state.

React has the concept of Controllable and Uncontrollable components.

  • Controllable components store very little state. They receive virtually everything as a prop and they expose eventHandlers to inform the parent when the "state" changes so the parent can render them again. As they don't store much state, they can't just setState to rerender. The parent has to do it.

  • Uncontrollable components will work for themselves. They store state, and when you change them, they are able to rerender without the intervention of their parents. Future rerenders of uncontrollable components will not have much effect on their state.

Your Radio is acting as an uncontrollable component. And that's why it's not responding to prop changes.

Community
  • 1
  • 1
Andre Pena
  • 56,650
  • 48
  • 196
  • 243
  • This doesn't actually solve the problem: firstly, the mixin updates the state. Secondly, removing all mentions of state, and making the component props driven doesn't change anything: the visible UI of the component does not get updated as it gets new props - though the console output confirms that render _does_ get called. – wfaler Jul 30 '15 at 18:03
1

It turns out this is not an issue with React, nor with the code above: It seems jquery.uniform.js, which is on the page somehow interferes with showing check and radio box updates properly.

Nasty.

wfaler
  • 166
  • 8