1

This post is very close to what I'm trying to do, but doesn't cover dynamic fields: Updating an object with setState in React

What I mean by dynamic fields: (Working Code)

import React, { Component } from 'react';

class ComponentName extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: "",
            age: "",
            email: "",
            manager: ""
        }
    }

    handleValueChange = (ev) => {
        this.setState({[ev.target.name]: ev.target.value}); // <----- dynamic field
    }

    handleSubmit = (ev) => {
        ev.preventDefault();
        console.log("Form Values =", this.state);
    }

    render() {
        <form onSubmit={ev => this.handleSubmit(ev)}>
            <div>
                <label>Name:</label>
                <input type="text" name="name" onChange={ev => this.handleValueChange(ev)} value={this.state.name} />
            </div>
            <div>
                <label>Age:</label>
                <input type="text" name="age" onChange={ev => this.handleValueChange(ev)} value={this.state.age} />
            </div>
            <div>
                <label>Age:</label>
                <input type="email" name="email" onChange={ev => this.handleValueChange(ev)} value={this.state.email} />
            </div>
            <div>
                <label>Age:</label>
                <input type="text" name="manager" onChange={ev => this.handleValueChange(ev)} value={this.state.manager} />
            </div>
            <button type="submit">Submit</button>
        </form>
    }
}

export default BadgeContractorRequest;

^Notice how I use this.setState({[ev.target.name]: ev.target.value}); to avoid hardcoding every field.


What I want to do... is update multiple form objects within the same state: (Broken Code)

import React, { Component } from 'react';

// Components
import FormOne from './formOne.js';
import FormTwo from './formTwo.js';
import FormThree from './formThree.js';

class ComponentName extends Component {
    constructor(props) {
        super(props);
        this.state = {
            formRequestTypeValue: "",
            formOne: {
                name: "",
                age: "",
                requestType: "",
                manager: ""
            },
            formTwo: {
                managerName: "",
                email: "",
                position: ""
            },
            formThree: {
                product: "",
                details: ""
            }
        }
    }

    formRequestTypeOnChangeHandler = (ev) => {
        this.setState({...this.state, formRequestTypeValue: ev.target.value});
    }

    handleValueChange = (ev) => {
        // This is where I'm Lost...
        this.state.formRequestTypeValue === "formOne" && this.setState({...this.state, formOne.[ev.target.name]: ev.target.value});
        this.state.formRequestTypeValue === "formTwo" && this.setState({...this.state, formTwo.[ev.target.name]: ev.target.value});
        this.state.formRequestTypeValue === "formThree" && this.setState({...this.state, formThree.[ev.target.name]: ev.target.value});
    }

    handleSubmit = (ev) => {
        ev.preventDefault();
        console.log("Form Values =", this.state);
    }

    render() {
        <form onSubmit={ev => this.handleSubmit(ev)}>
            <div>
                <select onChange={ev => this.formRequestTypeOnChangeHandler(ev)} value={this.state.formRequestTypeValue}>
                    <option value="">Please Select a Form</option>
                    <option value="formOne">Form One</option>
                    <option value="formTwo">Form Two</option>
                    <option value="formThree">Form Three</option>
                </select>
            </div>
            {
                this.state.formRequestTypeValue === "formOne" && 
                    <div>
                        <FormOne handleValueChange={this.handleValueChange} handleSubmit={this.handleSubmit} formValues={this.state.formOne} />
                    </div>
            }
            {
                this.state.formRequestTypeValue === "formTwo" && 
                    <div>
                        <FormTwo handleValueChange={this.handleValueChange} handleSubmit={this.handleSubmit} formValues={this.state.formTwo} />
                    </div>
            }
            {
                this.state.formRequestTypeValue === "formThree" && 
                    <div>
                        <FormThree handleValueChange={this.handleValueChange} handleSubmit={this.handleSubmit} formValues={this.state.formThree} />
                    </div>
            }
        </form>
    }
}

export default ComponentName;

The reason I'm not separating the <form> out into their own components, is because I want to maintain state of field values if the user selects a different form from the dropdown menu, and then goes back to their previous selection.


Here is where I'm stuck:

handleValueChange = (ev) => {
    // This is where I'm Lost...
    this.state.formRequestTypeValue === "formOne" && this.setState({...this.state, formOne.[ev.target.name]: ev.target.value});
    this.state.formRequestTypeValue === "formTwo" && this.setState({...this.state, formTwo.[ev.target.name]: ev.target.value});
    this.state.formRequestTypeValue === "formThree" && this.setState({...this.state, formThree.[ev.target.name]: ev.target.value});
}

Anyone have any idea what the syntax magic should be for handleValueChange?


Edit Update:

Also tried the following, but with no luck:

enter image description here

Fiddle Freak
  • 1,923
  • 5
  • 43
  • 83

1 Answers1

2

First:

You don't need to have . after formThree for example. Like below:

formThree[ev.target.name]

Then you need to have your past formOne fields to prevent them become empty. For that you need a code like below:

this.state.formRequestTypeValue === "formOne" &&
  this.setState((prevState) => ({
    ...prevState,
    formOne: {
      ...prevState.formOne,
      [ev.target.name]: ev.target.value,
    },
  }));

prevState is your last this.state that has not changes yet.

with formOne: {...prevState.formOne,[ev.target.name]: ev.target.value}, you are carrying old formOne fields and also overriding [ev.target.name] field.

You need to repeat this for other form, too.

Alireza HI
  • 1,873
  • 1
  • 12
  • 20