12
votes

I have a redux-form connected to my application state and everything seems to work great. I can fetch data and load it into my form, then submit data and get the metadata I want...

However, I have a custom interaction (a color picker) that needs to change the value of a managed Field on the fly. Everything I try will change the screen, but not the redux form state i.e. when I submit the form I just get the original field data and not the new data shown in the form.

The version below is passing the field props to the component and trying to use the ColorSelect component state as the field value. I have also tried creating an action creator, but same result and much much more code that this example...

Note: react@^15.4.2, react-redux@^5.0.2, redux-form@^6.4.3

ES6: CollectionForm.js

...
import ColorSelect from './ColorSelect';


class CollectionForm extends Component {

    /**
     * On form submit accepted
     * @param values
     */
    onSubmit(values){

        //See screenshot for evidence
        log('Updating collection:', 'warn', values);

        //values.color is grey and not yellow!
        this.props.dispatch(updateCollection(values));
        return;
    }

    /**
     * Form render
     * @return {JSX}
     */
    render()
    {
        const { handleSubmit, submitting } = this.props;

        return (
            <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
                <Field component={renderTextInput} name="name" type="text" label="Name"/>
                <Field component={ColorSelect} name="color" />

                <div class="field-buttons right-align group">
                    <Button type="submit" disabled={submitting} primary>
                        Save
                    </Button>
                </div>
            </form>
        );
    }
};

//Redux form decorator
CollectionForm = reduxForm({ form: 'collectionForm' })(CollectionForm)

// State connector
CollectionForm = connect(
    state => ({
        initialValues: state.collections.activeCollection

    }), //MapStatetoProps
    {
        onLoad: fetchCollection
    }   //mapActionToProps
)(CollectionForm);

export default CollectionForm;

ES6: CollectionForm.js

import React, { Component } from 'react'
import { Field, reduxForm, SubmissionError } from 'redux-form';

const colors = [
    'grey', 'green', 'yellow', 'orange', 'red', 'purple', 'blue'
];

export default class ColorSelect extends Component {

    constructor(props){
        super(props);

        this.state = {
            selectedColor : this.props.input.value //Set to current <Field> input value
        };

        this.onSelect = this.onSelect.bind(this);
        this.renderColor = this.renderColor.bind(this);
    }

    /**
     * Color picker selected
     * @param color
     */
    onSelect(color){
        this.setState({ selectedColor: color }); //Sets correct state here
    }

    /**
     * Render a color list item
     * @param color
     * @return {JSX}
     */
    renderColor(color){

        const select = this.state.selectedColor === color ? "active" : "";
        const klass = color + " " + select;

        return <li key={color}>
            <a class={klass} onClick={(event) => this.onSelect(color)}></a>
        </li>
    }

    /**
     * Render color list action
     * @return {JSX}
     */
    render(){

        //Override field value with colorSelected state
        
        return (
            <div>
                <input {...this.props.input} value={this.state.selectedColor} name="color" type="text" label="Color" />

                <div class="color-selector">
                    <ul>
                        {colors.map((color) => this.renderColor(color) )}
                    </ul>
                </div>
            </div>
        );
    }
}
1

1 Answers

16
votes

You can use react-redux's mapDispatchToProps together with the change action creator in order to achieve what you want:

import { Component } from "react";
import { connect } from "react-redux";
import { change } from "redux-form";

class ColorSelect extends Component {
  // ...other stuff in this class...

  renderColor (color) {
    const { selectColor } = this.props;
    return <li key={color}><a onClick={() => selectColor(color)}></a></li>;
  }
}

export default connect(null, {
  selectColor: color => change( "yourFormName", "yourFieldName", color )
})(ColorSelect)