2
votes

I have a React/Redux app and am trying to figure out the best way to build a proper onChange event for a controlled input field. React onChange fires onInput so I made an onBlur event to fire only when the user leaves the field. The only way I could think to implement my "onChange" code was to set the input value in React state. Then I could compare the old state to the new state that comes from the Redux store. However I don't think this is an ideal solution since the value has to be stored in both React state and the Redux store. Is there a way to implement a proper onChange for a component while only having the value stored in the Redux store?

/************Parent component************/
const mapStateToProps = (state) => {
    const fieldName = 'fieldName';
    const fieldData = formFieldSelector(fieldName)(state);

    return {
        fieldName,
        label: 'fieldName',
        value: fieldData.value
    }
};

const mapDispatchToProps = (dispatch) => ({
    onChangeEvent(fieldName, value) {
        dispatch(updateFieldValue(...))
    },
    onBlurEvent(fieldName, value) {
        dispatch(validateFormField(...)
    },
});

const MyFieldInput = connect(
    mapStateToProps,
    mapDispatchToProps
)(GenericInput);

/************Child Component************/
export default class GenericInput extends React.Component {

    constructor(props) {
        super(props);

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

    handleOnBlur (e) {
        const value = e.target.value;

        if(this.props.onBlurEvent && (value !== this.state.value)) {
            this.props.onBlurEvent(this.props.fieldName, value)
        }

        this.setState({value});
    }

    render () {
        const {
            fieldName,
            label = '',
            value = '',
            inputType = 'text',
            onChangeEvent,
            onBlurEvent,
        } = this.props;
        const onChange = onChangeEvent ? (e) => onChangeEvent(fieldName, e.target.value) : function () {};

        return (
            <div>
                <label>{label}
                    <input name={fieldName} type={inputType} value={value} onChange={onChange} onBlur={this.handleOnBlur.bind(this)} />
                </label>
            </div>
        )
    }
}
1

1 Answers

1
votes

Remove the onChange event code and dispatch the value of the input with onBlur using a ref.

So your input would look like this:

<input name={fieldName} type={inputType} value={value} ref="fieldRef" onBlur={this.handleOnBlur.bind(this)} />

And you will pass the value in handleOnBlur as follows:

this.props.onBlurEvent(this.props.fieldName, this.refs.fieldRef.value)