Stefan van de Vooren Stefan van de Vooren - 6 days ago 6
React JSX Question

React rebuild tree with HOC components (causing input field focus loss)

I'm using Higher Order Components to decorate my components.

const HOC = (WrappedComponent) => (props) => {
return (
<span>
<p>HOC Comp</p>
<WrappedComponent {...props}/>
</span>
)
}


I do like this pattern explained here: React Higher Order Components in depth

However I have a problem because the HOC causing React to recreate my component tree instead of updating the tree. This is nicely explained here React Reconciliation. HOC returns an anonymous function whereby React doesn't know it is actually rendering the same component. This is bad for performance and makes my input field lose focus.

How could I use HOC components without React recreating my tree on every
render()
?

Example code:

class Input extends React.Component {
componentWillMount() {
console.log('input component will mount');
}

componentWillUnmount() {
console.log('input component will unmount');
}

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

const HOC = (WrappedComponent) => {
const Help = (props) => {
return (
<span>
<WrappedComponent {...props}/>
<p>{props.help}</p>
</span>
)
};

return Help;
}

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

onChange(event) {
this.setState({value : event.target.value});
}

render() {
const Element = HOC(Input);
return (
<span>
<Element
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</span>
)
}
}

ReactDOM.render(
<MyComponent />,
document.getElementById('container')
);


See Fiddle example (see in your browser's console to see the mount and unmount logs from the input component every time you change the input and lose your focus)

Answer

You don't have to create Element in the render function. Instead you can create it in the constructor:

class MyComponent extends React.Component {
    constructor (props) {
        super(props);
        this.state = {value : 'start value'};
        this.element = HOC(Input);
    }
...

And use it in your render function like this:

        <span>
            <this.element
                value={this.state.value}
                onChange={this.onChange.bind(this)}
            />
        </span>

If needed you can update this.element in componentWillReceiveProps() or componentWillUpdate().

UPDATE: fiddle