11

I have:

  • A component App with a child component Filter.
  • The child needs to mutate state in the parent, which it is doing via an <input onChange={handler}>.
  • The handler is a prop that is set on the child by the parent.

All good so far.

However, whenever the a key is pressed on the input, it loses focus. I presume it's being destroyed and re-rendered.

If I hoist the Filter component up a level into the App and drive it off the state in that, then everything works as you'd expect, but obviously I'd like to be able to nest the components and share the state at the top level.

I guess calling setState at this higher level is causing the whole thing to get re-rendered, but I thought the diffing algorithm would be clever enough to avoid replacing the node in the Filter sub-component and thus avoid blurring the focus on the <input>.

What am I doing wrong / how can I fix this? Is there a better way to structure this?

Working JSBin here: http://jsbin.com/fexoyoqi/10/edit?html,js,output

var App = React.createClass({
  getInitialState: function() {
    return {
      items: ["Tom", "Dick", "Harry"],
      filterText: ''
    };
  },

  setFilterText: function (event) {
    this.setState({filterText: event.target.value});
  },

  render: function () {
    var filter = React.createClass({
      render: function () {
        return <input value={this.props.filterText} onChange={this.props.onChange}/>;
      }
    });

    var rows = this.state.items
      .filter(function (item) {
        return this.state.filterText == ''
          ? true
          : item.toLowerCase().indexOf(
              this.state.filterText.toLowerCase()) > -1;
      }.bind(this))
      .map(function(item) {
        return <li>{item}</li>
      });

    return (
      <div>
        Filter: <filter filterText={this.state.filterText}
          onChange={this.setFilterText}/>
        <ul>
          {rows}
        </ul>
      </div>
    );
  }
});

React.renderComponent(<App />, document.body);
Alastair Maw
  • 5,373
  • 1
  • 38
  • 50
  • Not that this superficially looks like [this other question](http://stackoverflow.com/questions/22573494/react-js-input-losing-focus-when-rerendering), but that doesn't have a nice self-contained working example and is doing slightly odd things, like storing state (and in fact entire components) in properties, I think. – Alastair Maw Aug 19 '14 at 14:31

1 Answers1

17

You're creating a new component class inside the render function.

Part of react's diffing algorithm looks at the components, and if it sees you rendered a different type component in one spot it says "the structure is probably significantly different, so I won't waste time diffing the children". It throws out the node, and renders the new result to the DOM.

Move var filter = React.createClass... somewhere it's only executed once, and it'll work fine.

Brigand
  • 84,529
  • 20
  • 165
  • 173
  • Aaaah. D'oh - that should have been obvious. Thanks! Working fix here, which declares the class only once: http://jsbin.com/fexoyoqi/12/edit?html,js,console,output – Alastair Maw Aug 19 '14 at 14:50