Avoiding re-renders for controlled inputs in a React Flux application

560 views Asked by At

I have a classic Flux pattern in my application, using fluxible. An input's value is tied to my component's prop, and its onChange invokes an action, which in turn updates a store, which in turn passes a prop into my component:

class MyComponent extends React.Component {
  handleChange(e) {
    this.context.executeAction(persist, { value: e.target.value });
  }

  render() {
    return <input value={this.props.value} onChange={this.handleChange.bind(this)}/>;
  }
}

The problem I'm having is that when the store emits a change and the new value gets passed into my component, that causes it to re-render, and the input cursor position is lost (is moved to the end).

I know I can create a custom shouldComponentUpdate method for my component, but doing so is somewhat tricky and definitely tedious for what I assume is a very frequently-occuring pattern.

Is there a well-established pattern for avoiding re-renders for controlled inputs?

1

There are 1 answers

2
Jacob On BEST ANSWER

I found the description of ReactLink to give a pretty good pattern for dealing with this sort of thing. Here's what I came up with:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: props.value };
    this.handleChange = this.handleChange.bind(this);
  }

  componentWillReceiveProps(newProps) {
    // If in some cases you want to handle a new value prop, update state here
  }

  handleChange({ target: { value } }) {
    this.setState({ value });
    this.context.executeAction(persist, { value });
  }

  render() {
    return <input value={this.state.value} onChange={this.handleChange}/>;
  }
}

Basically, just ignore the people that say "if you set state based on a prop you're doing it wrong" and set your initial state based on the prop. This state is what your input now uses for its value. Then, assuming that new value props do come in, they won't affect your render output. If you do want them to, you can add logic to componentWillReceiveProps. In my case, for example, I did want to update the input, but only if it wasn't focused:

componentWillReceiveProps({ value }) {
  if (!this.state.isFocused) {
    this.setState({ value });
  }
}