Nick Meldrum Nick Meldrum - 3 months ago 35
React JSX Question

React Higher Order Component forces re-render of wrapped component

I am struggling to understand how to correctly implement this validation behaviour in a higher order component.

===========================================

EDIT: TLDR: Thanks to user @noa-dev 's excellent suggestion I have created a React Fiddle here: https://jsfiddle.net/8nLumb74/1/ to show the issue.

Simply put: Why does my textbox lose focus on editing when wrapped by this HOC?



What am I doing wrong?

The Textbox component:

import React from 'react'

export default React.createClass({
changeText(e) {
if (this.props.validate)
this.props.validate(e.target.value)
this.props.update(e.target.value)
},
componentDidMount() {
console.log('should only be fired once')
},
render() {
return (<input type="text"
value={this.props.text}
onChange={this.changeText} />)
}
})


The Validator component:

import React from 'react'

export default function (WrappedComponent) {
const Validation = React.createClass({
validate(text) {
console.log('validating', text)
},
render() {
return (
<WrappedComponent
{...this.props}
validate={this.validate}
/>
)
}
})
return Validation
}


The parent Form component:

import React from 'react'
import TextBox from './text-box'
import Validator from './validator'

export default React.createClass({
getInitialState() {
return ({text: 'oh hai'})
},
update(text) {
this.setState({text})
},
render() {
const ValidatingTextBox = Validator(TextBox)
return (<ValidatingTextBox
text={this.state.text}
update={this.update} />)
}
})

Answer

In the render method of the Form component, you are creating a new ValidatingTextBox every time:

    render() {
        const ValidatingTextBox = Validator(TextBox)
        return (<ValidatingTextBox
            text={this.state.text}
            update={this.update} />)
    }

Instead, you should make the component and then use it so the instance gets maintained. A possible Form component would look like:

import React from 'react'
import TextBox from './text-box'
import Validator from './validator'

const ValidatingTextBox = Validator(TextBox) 

export default React.createClass({
    getInitialState() {
        return ({text: 'oh hai'})
    },
    update(text) {
        this.setState({text})
    },
    render() {
        return (<ValidatingTextBox
            text={this.state.text}
            update={this.update} />)
    }
})
Comments