简体   繁体   中英

How to pass value from custom component class to redux-form?

I'm calling a custom component in my redux-form.

<Field name="myField" component={SiteProjectSelect}/>

This component is a combination of two combo boxes. The second box is dependant on the value of the first on - ie depending on what site you select, you can choose from a list of projects. What I'd like to do is get the form to receive the selected site and the selected projects. However, I'm not sure how to pass the values to the redux-form.

class SiteProjectSelect extends Component {
    constructor() {
        super();
        this.state = {
            selectedSite: null,
            selectedProject: null,
        };
    }

    handleSiteSelection = selectedSite => {
        console.log(selectedSite)
        this.setState({ selectedSite, selectedProject: null });
    };

    handleProjectSelection = selectedProject => {
        this.setState({ selectedProject });
        this.props.input.onChange(selectedProject.value); 
    };

    render() {
        const selectedRow = this.state.selectedSite ? projects.find((node) => node.site === this.state.selectedSite.value) : "";
        const filteredProjectOptions =  selectedRow ? selectedRow.projects.map(project => ({ value: project, label: project })) : []

        return (
            <div {...this.props} >
                <label>Site</label>
                <div style={{ marginBottom: '20px' }} >
                    <Select
                        name="site"
                        value={this.state.selectedSite}
                        onChange={this.handleSiteSelection}
                        options={siteOptions}
                        isSearchable
                    />
                </div>
                <div style={{ marginBottom: '20px' }} >
                    <label>Project</label>
                    <Select
                        name="project"
                        value={this.state.selectedProject}
                        onChange={this.handleProjectSelection}
                        options={filteredProjectOptions}
                        isMulti
                        isSearchable
                        closeMenuOnSelect={false}
                    />
                </div>
            </div>
        );
    }
}

I did finally figure it out. For anyone else who stumbles across this, here's what I needed to know. To use a custom component,

  • Use the onChange prop to set the new value of the Field. You do this by calling the onChange function, this.props.input.onChange(your-components-new-value-here) when you need to change the value of the component and passing it the new value.
  • This new value will now be stored in the value prop: this.props.input.value . So, wherever in the render function for your component you need to pass/display the current value of your component, use the value prop. It has to be the value prop and not another variable such as what you passed to your onChange function. What this does is give control of what's displayed to the state of your redux-form which the value prop is tied to. Why is this useful? For example, you could take the user to a form review page when they're done and then back to the form if the user wants to make some more changes. How would redux-form know how to repopulate all of what's displayed without getting the user to fill in the form again? Because the display is dependant on the state, not user input! Took me a while to make sense of all this!!

In my example, where I was using two react-select components, one of which was dependant on the other, I ended up having to use the Fields component which allowed me to have two Fields in my component rather than just the one. Once I implemented this, it also became evident that I didn't need to have a separate state within my component as the value of both Fields is always accessible via the value prop for each of them. So, yes, I could have just used a stateless function after all!

I call my component with:

<Fields names={["site", "projects"]} component={SiteProjectSelect} />

My final working component:

class SiteProjectSelect extends Component {
    handleSiteSelection = selectedSite => {
        this.props.site.input.onChange(selectedSite);
        this.props.projects.input.onChange(null);
    };

    handleProjectSelection = selectedProjects => {
        this.props.projects.input.onChange(selectedProjects);
    };

    renderSite = () => {
        const {
            input: { value },
            meta: { error, touched }
        } = this.props.site;

        return (
            <div>
                <label>Site</label>
                <div style={{ marginBottom: '20px' }}>
                    <Select
                        name="site"
                        value={value}
                        onChange={this.handleSiteSelection}
                        options={siteOptions}
                        isSearchable
                    />
                </div>
                <div className="red-text" style={{ marginBottom: '20px' }}>
                    {touched && error}
                </div>
            </div>
        );
    };

    renderProjects = () => {
        var {
            input: { value },
            meta: { error, touched }
        } = this.props.projects;

        const selectedSite = this.props.site.input.value;

        const selectedRow = selectedSite
            ? projects.find(node => node.site === selectedSite.value)
            : '';
        const filteredProjectOptions = selectedRow
            ? selectedRow.projects.map(project => ({
                    value: project,
                    label: project
              }))
            : [];

        return (
            <div>
                <div style={{ marginBottom: '20px' }}>
                    <label>Projects</label>
                    <Select
                        name="projects"
                        value={value}
                        onChange={this.handleProjectSelection}
                        options={filteredProjectOptions}
                        isMulti
                        isSearchable
                        closeMenuOnSelect={false}
                    />
                </div>
                <div className="red-text" style={{ marginBottom: '20px' }}>
                    {touched && error}
                </div>
            </div>
        );
    };

    render() {
        return (
            <div>
                {this.renderSite()}
                {this.renderProjects()}
            </div>
        );
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM