user8306074
user8306074

Reputation: 395

How to save input entered by the user in a state which is an array in React

The whole idea is to take users input in a form and display their input in a JSON object. In state I have an array and inside it another array.

My Form.js looks like this,

state= {
    groups:[{
      typeA:[{}],
      typeB:[{}]
    }],
    credentials: false
    };

    change = e =>{
        this.setState({[e.target.name]: e.target.value})
    };

    handleSubmit(event) {
        event.preventDefault();
        this.setState({
          credentials: true 
        });
      }
render(){
        return(
            <div class="classform">
            <form >
                <label>
                    Field1:
                    <br/>
                    <input type="text"
                    name="typeA"
                    placeholder="Type A"
   //store it as the first element of the type A
                    value={this.state.groups.typeA[0]} 
                    onChange={this.change.bind(this)}
                 />
  //other fields with the same code

Subsequently, Field2 will be stored as the second element of type A Then, Field3 and Field4 will be stored as 2 of type B array

I expect the code to give me an output like :

"typeA": ["field1 value", "field2 value"],
"typeB": ["field3 value", "field4 value"]}

I'm a beginner with React and I'm not able to store the field values in the state which is an array.

Upvotes: 3

Views: 11580

Answers (3)

Junius L
Junius L

Reputation: 16152

Give each input a custom attribute, for example data-group="typeA". In your on change function get that value and add the values to the correct array.

<input
  type="text"
  name="col2"
  placeholder="Type A"
  data-group="typeA" // add custom attribute typeA | typeB etc.
  onChange={e => this.change(e)}
/>

In your change handle get the custom attribute, and use it to add the value to the correct array.

change = e => {

  // create a copy of  this.state.groups
  const copyGroups = JSON.parse(JSON.stringify(this.state.groups));

  // get data-group value
  const group = event.target.dataset.group;

  if (!copyGroups[0][group]) {
    copyGroups[0][group] = []; // add type if it doesn't exists
  }

  const groups = copyGroups[0][group];
  const index = this.findFieldIndex(groups, e.target.name);

  if (index < 0) {
    // if input doesn't exists add to the array
    copyGroups[0][group] = [...groups, { [e.target.name]: e.target.value }];
  } else {
    // else update the value
    copyGroups[0][group][index][e.target.name] = e.target.value;
  }

  this.setState({ groups: copyGroups });
};

pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>

<script type="text/babel">

function formatState(json) {
  if (typeof json != 'string') {
       json = JSON.stringify(json, undefined, 2);
  }
  json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => match);
}

const Credentials = ({ value }) => {
  return <pre>{formatState(value)}</pre>;
};

class App extends React.Component {
  state = {
    groups: [
      {
        typeA: [],
        typeB: []
      }
    ],
    credentials: false
  };

  handleSubmit = event => {
    event.preventDefault();

    this.setState({
      credentials: true // display Credentials  component
    });
  };

  // get the current input index in the array typeA | typeB
  findFieldIndex = (array, name) => {
    return array.findIndex(item => item[name] !== undefined);
  };

    change = e => {

      // create a copy of  this.state.groups
      const copyGroups = JSON.parse(JSON.stringify(this.state.groups));

      // get data-group value
      const group = event.target.dataset.group;
      
      if (!copyGroups[0][group]) {
        copyGroups[0][group] = []; // add new type
      }
      
      const groups = copyGroups[0][group];
      const index = this.findFieldIndex(groups, e.target.name);

      if (index < 0) {
        // if input doesn't exists add to the array
        copyGroups[0][group] = [...groups, { [e.target.name]: e.target.value }];
      } else {
        // update the value
        copyGroups[0][group][index][e.target.name] = e.target.value;
      }

      this.setState({ groups: copyGroups });
    };
    
 removeKey = (key) => {
    const temp = {...this.state};
    delete temp[key];
    return temp;
 }

  render() {
    return (
      <div>
        <input
          type="text"
          name="col1"
          placeholder="Type A"
          data-group="typeA"
          onChange={e => this.change(e)}
        />

        <input
          type="text"
          name="col2"
          placeholder="Type A"
          data-group="typeA"
          onChange={e => this.change(e)}
        />

        <input
          type="text"
          name="col2"
          placeholder="Type B"
          data-group="typeB"
          onChange={e => this.change(e)}
        />
        
        <input
          type="text"
          name="typec"
          placeholder="Type C | New Type"
          data-group="typeC"
          onChange={e => this.change(e)}
        />
        
        <input
          type="text"
          name="typed"
          placeholder="Type D | New Type"
          data-group="typeD"
          onChange={e => this.change(e)}
        />

        <button onClick={this.handleSubmit}>Submit</button>
        {this.state.credentials && (
          <Credentials value={JSON.stringify(this.removeKey('credentials'), undefined, 2)} />
        )}
      </div>
    );
  }
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
);
</script>

Upvotes: 1

samad324
samad324

Reputation: 229

try this:

import React, { Component } from "react";

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      groups: [
        {
          typeA: [{}],
          typeB: [{}]
        }
      ],
      credentials: false
    };
  }

  change = (e, key) => {
    let { groups } = this.state;

    let myVal = groups[0][e.target.name];
    // or if you want to add value in an object likr [{value: 'abcd'}]
    myVal[0][key] = e.target.value;
    groups[0][e.target.name] = myVal;
    console.log("TCL: App -> groups", groups);
    this.setState({ groups });
  };

  render() {
    return (
      <div>
        <input
          type="text"
          name="typeA"
          placeholder="Type A"
          value={this.state.groups[0].typeA[0].value2}
          onChange={e => this.change(e, "value2")}
        />
        <input
          type="text"
          name="typeA"
          placeholder="Type A2"
          value={this.state.groups[0].typeA[0].value}
          onChange={e => this.change(e, "value")}
        />
        <br />
        <input
          type="text"
          name="typeB"
          placeholder="Type b"
          value={this.state.groups[0].typeB[0].value}
          onChange={e => this.change(e, "value")}
        />
      </div>
    );
  }
}


Upvotes: 2

sarabs3
sarabs3

Reputation: 594

For the sake of simplicity, I will recommend below solution, Instead of having a nested array in the state, you can manage the input values in the different state variables and at the time of submitting, change them to the output you want.

state = {
  field1: '',
  field2: '',
  field3: '',
  field4: '',
}
change = e => {
  this.setState({[e.target.name]: e.target.value})
};

handleSubmit(event) {
  event.preventDefault();
  const { field1, field2, field3, field4 } = this.state;
  // This is your output
  const output = [{typeA: [field1, field2], typeB: [field2, field3]}];
  this.setState({
    credentials: true 
  });
}
render(){
  return(
    <div class="classform">
      <form >
        <label>
          Field1:
          <br/>
          <input type="text"
            name="field1"
            placeholder="Type A"
            value={this.state.field1}
            onChange={this.change}
          />
        </label>
        <label>
          Field2:
          <br/>
          <input type="text"
            name="field2"
            placeholder="Type A"
            value={this.state.field2}
            onChange={this.change}
          />
        </label>

Upvotes: 3

Related Questions