keykan
keykan

Reputation: 95

How to stop re-rendering all Childs in React render function when I change just one child value?

I have some Card (more than 10 Card component) in a Cards component and each Card has a form with more than 10 textField components. When I'm typing in textFields, It has delay between type and update value of textField. After spending more than 2 days, I found my problem. I think it's related to re-rendering all Childs (all Card components) when I set my value in statein value update... .

I want to know where am I wrong? If my codes is standard, is there any way to stop re-rendering all Childs after changing state for just one textField?

My codes are like as follow:

MainComponent:

 export default class MainComponent extends Component {

  constructor(props) {
    super(props);

    this.state = {
      value : {}
    };
  }

  static PropTypes = {
    results: PropTypes.array.isRequired
  };

  handleChange(ref, e) {
    this.state.value[ref] = e;
    this.setState(this.state);
  }

  render() {
    const { results } = this.props;

        <Cards>
        { 
        results.map((result, index) => {
            var ref_taxtfield1 = result.id + "taxtfield1";
            var ref_taxtfield2 = result.id + "taxtfield2";
            var ref_taxtfield3 = result.id + "taxtfield3";
            var ref_taxtfield4 = result.id + "taxtfield4";
            var ref_taxtfield5 = result.id + "taxtfield5";

            return <Card key={ result.id } style={ styles.card }>

                <Form>
                    <div style={ styles.innerContainer }>
                        <Textfield
                        name="taxtfield1"
                        label="My Label 1"
                        ref={ref_taxtfield1}
                        onValueChange={this.handleChange.bind(this, ref_taxtfield1)}
                        value={this.state.value[ref_taxtfield1]}
                        />
                        <Textfield
                        name="taxtfield2"
                        label="My Label 2"
                        ref={ref_taxtfield2}
                        onValueChange={this.handleChange.bind(this, ref_taxtfield2)}
                        value={this.state.value[ref_taxtfield2]}
                        />
                        <Textfield
                        name="taxtfield3"
                        label="My Label 3"
                        ref={ref_taxtfield3}
                        onValueChange={this.handleChange.bind(this, ref_taxtfield3)}
                        value={this.state.value[ref_taxtfield3]}
                        />
                        <Textfield
                        name="taxtfield4"
                        label="My Label 4"
                        ref={ref_taxtfield4}
                        onValueChange={this.handleChange.bind(this, ref_taxtfield4)}
                        value={this.state.value[ref_taxtfield4]}
                        />
                        <Textfield
                        name="taxtfield5"
                        label="My Label 5"
                        ref={ref_taxtfield5}
                        onValueChange={this.handleChange.bind(this, ref_taxtfield5)}
                        value={this.state.value[ref_taxtfield5]}
                        />
                    </div>
                </Form>
            </Card>})}
        </Cards>
        }
}

My TextField Component

export default class Textfield extends Input {

  static defaultProps = {
    initialCount: 0,
    value: "",
    defaultValue: "",
    onValueChange: null,
    label: ""
  };
  state = { focused: false };

  onChange = this.onChange.bind(this);
  onChange(e) {
    if(this.props.onValueChange){
      this.props.onValueChange(e.target.value);
    }
  }

  handleOnBlur = this.handleOnBlur.bind(this);
  handleOnBlur(e){
    this.setState({focused: false});
    if(this.props.onBlur){
      this.props.onBlur(e);
    }
  }

  render() {
    const { focused } = this.state;
    const { value, disabled } = this.props;

    return (
      <div>
        <label>{this.props.label}</label>
        <input
          { ...this.inputProps() }
          type="text"
          placeholder={this.props.placeholder}
          defaultValue={this.props.defaultValue}
          onChange={this.onChange}
          onBlur={this.handleOnBlur}
          value={ isCurrency ? formatData.currency(value) : value}
          disabled={disabled}
        />
      </div>
    )
  }

}

My Card and Cards Component

export class Cards extends Component {
    render() {
    const { children, ...props } = this.props;

        return <div {...props} >{ children }</div>;
    }
};

export class Card extends Component {
  render() {
    const { ...props } = this.props;

    return <div {...props} } />
  }
}

I use ES6 syntax and also remove all style tags from my code to simplify.

Upvotes: 1

Views: 523

Answers (1)

Felix Kling
Felix Kling

Reputation: 816424

You are passing a new function to every Textfield component on render:

onValueChange={this.handleChange.bind(this, ref_taxtfield1)}

.bind returns a new function every time, causing every Textfield to render on each update.

Two possible solutions:

  • Don't use .bind inside .render. .bind the method once in the constructor and let Textfield pass an identifier to this.props.onValueChange.
  • Implement shouldComponentUpdate in Textfield, returning false if only this.props.onValueChange changed.

Upvotes: 2

Related Questions