0

My react application take data from user and should output them when user click on SUBMIT button. Now user open the application appears a number input. There he can set a number, for example 2. After that appears 2 boxes, where user can add fields cliking on the button +. In each field user have to add his name and last name. Now the application works, but no in the right way.
Now, if user add his user name and last name in first box and click on SUBMIT, everithing appears in:

 const onFinish = values => {
    const res = {
      data: [dataAll]
    };
    console.log("Received values of form:", res);
  };

But if user add one another inputs clicking on add fields button, the first data dissapears. The same issue is when user add data in 2 boxes, the data is saved just from the last box.

function handleInputChange(value) {
    const newArray = Array.from({ length: value }, (_, index) => index + 1);
    setNr(newArray);
  }

  const onFinish = values => {
    const res = {
      data: [dataAll]
    };
    console.log("Received values of form:", res);
  };

  return (
    <div>
      <Form name="outter" onFinish={onFinish}>
        {nr.map(i => (
          <div>
            <p key={i}>{i}</p>
            <Child setDataAll={setDataAll} nr={i} />
          </div>
        ))}
        <Form.Item
          name={["user", "nr"]}
          label="Nr"
          rules={[{ type: "number", min: 0, max: 7 }]}
        >
          <InputNumber onChange={handleInputChange} />
        </Form.Item>
        <Form.Item>
          <Button htmlType="submit" type="primary">
            Submit
          </Button>
        </Form.Item>
      </Form>
    </div>
  );
};

Mu expected result after clicking on submit is:

data:[
       {
  nr:1,
  user: [
      {
      firstName:'John',
      lastName: 'Doe'
      },
      {
      firstName:'Bill',
      lastName: 'White'
      }
       ]
       },
              {
  nr:2,
  user: [
      {
      firstName:'Carl',
      lastName: 'Doe'
      },
      {
      firstName:'Mike',
      lastName: 'Green'
      },
      {
      firstName:'Sarah',
      lastName: 'Doe'     
      }
       ]
       }, 
     ]
   ....  
     // object depend by how many fields user added in each nr

Question: How to achieve the above result?
demo: https://codesandbox.io/s/wandering-wind-3xgm7?file=/src/Main.js:288-1158

1 Answer 1

1

Here's an example how you could do it.

As you can see Child became Presentational Component and only shows data.

Rest of logic went to the Parent component.

const { useState, useEffect, Fragment } = React;

const Child = ({nr, user, onAddField, onChange}) => {

  return <div>
    <div>{nr}</div>
    {user.map(({firstName, lastName}, index) => <div key={index}>
    <input onChange={({target: {name, value}}) => onChange(nr, index, name, value)} value={firstName} name="firstName" type="text"/>
    <input onChange={({target: {name, value}}) => onChange(nr, index, name, value)} value={lastName} name="lastName" type="text"/>
    </div>)}
    <button onClick={() => onAddField(nr)}>Add Field</button>
  </div>
}

const App = () => {
  const [items, setItems] = useState([
  {
    nr: 1,
    user: []
  },
  {
    nr: 2,
    user: [
      {firstName: 'test1', lastName: 'test1'},
      {firstName: 'test2', lastName: 'test2'}
    ]
  }
  ]);  

  const onCountChange = ({target: {value}}) => {
    
    setItems(items => {
      const computedList = Array(Number(value)).fill(0).map((pr, index) => ({
        nr: index + 1,
        user: []
      }))
      
      const merged = computedList.reduce((acc, value) => {
        const item = items.find(pr => pr.nr === value.nr) || value;
        
        acc = [...acc, item];
        
        return acc;
      }, [])
      
      return merged;
    })
    
  }

  const onChildChange = (nr, index, name, value) => {
    setItems(items => {
      const newItems = [...items];
      const item = newItems.find(pr => pr.nr === nr);
      const field = item.user.find((pr, ind) => index === ind)
      field[name] = value;
      
      return newItems;
    });
  }
  
  const onAddField = (nr) => {
    setItems(items => {

      const newItems = [...items];
      const item = newItems.find(pr => pr.nr === nr);
      item.user = [...item.user, {
        firstName: '',
        lastName: ''
      }];

      return newItems;
    })
  }

  const onClick = () => {
    console.log({data: items});
  }

  return <div>
    {items.map((pr, index) => <Child {...pr} onAddField={onAddField} onChange={onChildChange} key={index} />)}
    <input onChange={onCountChange} value={items.length} type="number"/>
    <button onClick={onClick}>Submit</button>
  </div>
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
  );
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

Sign up to request clarification or add additional context in comments.

8 Comments

it looks great, but how to add delete functionality if user added 2 fields after clicking Add Field, and now he wants to remove one pair of fields?
then child should expose another method like 'onDeleteField' which accepts params nr, index and inside it there would be setItems(items => { which computes new item with user array filtered
also, i can't figure out why if i click on Add Field the second time, the code adds 2 pairs on finputs not one. Could you change? Thanks in advance
The input pair is for firstname and lastname as was in example
first time when i click on Add Field it works ok, but if i want to click second time it add: 2 inputs with user name and 2 with lastName, but should be 1 with user name and 1 with last name. Please, tell me if you understand what i want to say. Thanks
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.