Reinier68
Reinier68

Reputation: 3242

How to update correct text input values in a nested array of objects?

I am new to React and got a problem. I got a big a object that will contain a lot of logic for the application I am trying to build.

Based on this object I am trying to render inputfields and also update the corresponding records in the object. I got a Codesandbox with my problem setup here.

I got my 'top level' component that holds the object and passes it down to its children:

import "./styles.css";
import { useState } from "react";
import Content from "./Content";

export default function App() {
  const [formDocument, setFormDocument] = useState({
    document: {
      content: {
        sections: [
          {
            additionalFields: true,
            destroyable: false,
            key: "personalDetails",
            moveable: false,
            fields: [
              {
                fieldType: "text",
                key: "name",
                linkedFieldsKey: "name"
              },
              {
                fieldType: "text",
                key: "emailAddress",
                linkedFieldsKey: "emailAddress"
              }
            ]
          }
        ],
        records: [{ key: "1", values: ["Tim", "[email protected]"] }]
      }
    }
  });

  const updateDocument = (e) => {
    //how do I update the corresponding records for the inputfields?
    console.log("inputfield is updated");
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <Content {...formDocument} updateDoc={(e) => updateDocument(e)} />
    </div>
  );
}

Now I got a child that uses the formDocument object called Content.jsx and I want to update the corresponding records in the formDocument object. This is the Content.jsx:

export default function Content({ updateDoc, ...formDocument }) {
  return (
    <div>
      <input
        style={{ display: "block", marginTop: "10px" }}
        type="text"
        value={formDocument.document.content.records[0].values[0]}
        onChange={(e) => updateDoc(e)}
      />

      <input
        style={{ display: "block", marginTop: "10px" }}
        type="text"
        value={formDocument.document.content.records[0].values[1]}
        onChange={(e) => updateDoc(e)}
      />
    </div>
  );
}

How do I update the correct records without making a function for each and every inputfield?

Upvotes: 0

Views: 933

Answers (2)

Gonzalo
Gonzalo

Reputation: 358

When updating an object/array in React, you have to make sure you update it with a "new" item. For doing that, you could create a copy like this:

const arrayCopy = [...array]
const objectCopy = {...object}

By updating the state using the same object or array, React won't kow that element has been updated.

So for example, if you have this in the state:

const [document, setDocument] = useState({
  users: [
    { name: 'Peter', email: '[email protected]' },
    { name: 'Michael', email: '[email protected]' },
  ]
})

Then I would follow these steps in order to update the state properly:

// Create a copy of the object in the state
let updatedObject = {...document}

// Update it as needed
let newUsers = [...document.users]
newUsers[1].name = 'Manolo'

const updatedObject = {
  ...document,
  users: [...newUsers]
}

// Update the state with a copy of the new object
// For doing so, I'd pass a function callback
setUsers(() => ({ ...updatedObject }))

That should make rerender your component and see the changes.


I would also reorganize the state a little bit. Split it when needed, you can use more than one state variable. That will help you locate the errors and debug it better. Also improves readability and will let you write better code.

Upvotes: 1

Ankush Verma
Ankush Verma

Reputation: 778

I do think that you should spit the States but the code for this goes as follows :-

import "./styles.css";
import { useState } from "react";
import Content from "./Content";

export default function App() {
  const [formDocument, setFormDocument] = useState({
    document: {
      content: {
        sections: [
          {
            additionalFields: true,
            destroyable: false,
            key: "personalDetails",
            moveable: false,
            fields: [
              {
                fieldType: "text",
                key: "name",
                linkedFieldsKey: "name"
              },
              {
                fieldType: "text",
                key: "emailAddress",
                linkedFieldsKey: "emailAddress"
              }
            ]
          }
        ],
        records: [{ key: "1", values: ["Tim", "[email protected]"] }]
      }
    }
  });

  const updateDocument = (e, index) => {
    //how do I update the corresponding records for the inputfields?
    let new_document = {...formDocument};
    new_document['document']['content']['records'][0]['values'][index]=e.target.value;
    setFormDocument(new_document);
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <Content {...formDocument} updateDoc={updateDocument} />
    </div>
  );
}

The Content.js

export default function Content({ updateDoc, ...formDocument }) {
  return (
    <div>
      {formDocument.document.content.records[0].values.map((elem, i) => (
        <input
          style={{ display: "block", marginTop: "10px" }}
          type="text"
          value={elem}
          onChange={(e) =>
            updateDoc(e,i)
          }
        />
      ))}
    </div>
  );
}

Upvotes: 2

Related Questions