3

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.

user8306074
  • 395
  • 4
  • 9
  • 19

3 Answers3

3

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>
sarabs3
  • 504
  • 7
  • 13
2

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>
    );
  }
}


samad324
  • 199
  • 6
  • This gives me an error TypeError: Unable to get property '0' of undefined or null reference. Do I need to change the "name" inside the input tag as well – user8306074 May 09 '19 at 05:43
  • Still giving the same error. I also tried myVal = groups[e.target.name][0] (so that it takes the name="typeA" then inserts into [0] index ). Do you think I should use a map to assign the array values ? – user8306074 May 09 '19 at 05:59
  • would you please try again! – samad324 May 09 '19 at 06:15
  • Nope! still not working :( It isnt even going inside the return . Gives the same error "TypeError: Unable to get property '0' of undefined or null reference" – user8306074 May 09 '19 at 06:43
  • Wow! It worked! Just one thing, for subsequent columns, like col2 (second element for typeA array) and col 3 and col 4 (2 elements in typeB array). How should change the values? Suppose I wrap the
    inside the map function but how should I change the values index inside chnage function
    – user8306074 May 09 '19 at 07:38
  • For next input box, It is overriding the previous value. It is not creating another value for next input – user8306074 May 09 '19 at 10:15
  • now check again! its work for me! I think you forget to change the value(`typeA` || `typeB`) in value attribute! – samad324 May 09 '19 at 10:23
  • Sorry, for the misunderstanding, that part is working. But If I add another field of of typeA. Then it overwrites the value. For achieving this, "typeA": ["field1 value", "field2 value"]. I need one more field for typeA. but it is taking only one value and not appending the new value to the array – user8306074 May 09 '19 at 10:38
  • why does it gives me this error: "Cannot read property 'value' of undefined" when I try to run "value={this.state.filters[0].company_meta[1].value}". Ideally, it should save it into the next element of the array. – user8306074 May 09 '19 at 12:40
  • now check again! now it will solve your field replacement problem! – samad324 May 09 '19 at 16:08
  • and if you want to get value then you should write `value={this.state.groups[0].typeA[0].value}`because their is no second index is arrays! so the `0` will only available! – samad324 May 09 '19 at 16:09
1

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>
Junius L
  • 15,881
  • 6
  • 52
  • 96
  • Omg! Cool! Thanks! Can you tell me how to print out the JSON object without printing credentials: true? I just used it to print out the JSON object – user8306074 May 13 '19 at 04:43
  • Use delete https://stackoverflow.com/questions/3455405/how-do-i-remove-a-key-from-a-javascript-object – Junius L May 13 '19 at 04:46
  • Okay, I see! Thanks – user8306074 May 13 '19 at 06:23
  • Yea I got it! Thanks for you help – user8306074 May 13 '19 at 10:11
  • Hi Junis, I had a question which is just an extention to this question. I want to know if there is a way, to store the input values as an array (and just the values). Suppose, if I have a column start date and end date: I want to store the values in the form of an array. So, I need an output like: typeA: [ [col1value1, col1value2], [startdate, enddate]]. I know it is complicated, but I the output should look like this. Doesnt matter,even if I'm not storing it actually in an array – user8306074 May 13 '19 at 10:17
  • @user8306074 yes you can. – Junius L May 13 '19 at 10:54
  • For doing this, should I customise the JSON output or should find a way to store the input values into an array ? – user8306074 May 13 '19 at 10:59
  • Can you tell me one more thing, if possible, in this example you are storing all the input field values in an array; like typeA and typeB are arrays, how do I convert them into a list? I dont want it as an array I want it as a list because it will help me in reaching the desired output – user8306074 May 14 '19 at 00:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/193296/discussion-between-junius-l-and-user8306074). – Junius L May 14 '19 at 03:23