A
A
andrey_chirkin2021-11-22 15:37:34
JavaScript
andrey_chirkin, 2021-11-22 15:37:34

Why is data changing in two states at the same time?

Good afternoon! I am trying to make a table with editable fields. The idea is that when you click on the save button, the data from the input should be saved in the table, and when you click on the cancel button, the data in the table should not change. I have a problem with the contacts and contactsInput states. The first state is needed in order to display data in a table. The second is for the value field in . When data is written to one state, it is written to another state. Although, as far as I understand, they do not have common links. Thanks in advance!

import React, {Fragment, useState} from 'react'

export default function ContentTable() {

  const data = [
    {
      "id": 1,
      "fullName": "Jenny Chan",
      "address": "3 waterfoot road",
      "phoneNumber": "333-962-7516",
      "email": "[email protected]"
    },
    {
      "id": 2,
      "fullName": "Jessica warren",
      "address": "4 tall town",
      "phoneNumber": "011-211-7516",
      "email": "[email protected]"
    },
    {
      "id": 3,
      "fullName": "Tony Frank",
      "address": "11 lesly road",
      "phoneNumber": "788-962-7516",
      "email": "[email protected]"
    },
    {
      "id": 4,
      "fullName": "Jeremy Clark",
      "address": "333 miltown manor",
      "phoneNumber": "011-962-111",
      "email": "[email protected]"
    },
    {
      "id": 5,
      "fullName": "Raymond Edwards",
      "address": "99 blue acres",
      "phoneNumber": "3231-962-7516",
      "email": "[email protected]"
    }
  ]

  const [contacts, setContacts] = useState([...data])
  const [contactsInput, setContactsInput] = useState([...data])
  const [checkId, setCheckId] = useState(null)

  const handleChange = (index) => (
    (event) => {
      const {name, value} = event.target
      const contactsInputCopy = [...contactsInput]
      contactsInputCopy[index][name] = value
      console.log(index, name)
      console.log(contactsInputCopy[index][name])
      console.log(contacts[index][name])
      setContactsInput(contactsInputCopy)
    }
  )

  const handleSave = () => {
    setContacts([...contactsInput])
    setCheckId(null)
  }

  const handleCancel = (event) => {
    setCheckId(null)
  }

  return (
    <div className="tableContainer">
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Address</th>
            <th>Phone number</th>
            <th>email</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
        {contacts.map((contact, index) => (
          <Fragment key={`key:${index}`}>
            {checkId === contact.id ? (
              <tr>
                <td>
                  <input type="text" name="fullName" value={contactsInput[index].fullName} onChange={handleChange(index)}/>
                </td>
                <td>
                  <input type="text" name="address" value={contactsInput[index].address} onChange={handleChange(index)}/>
                </td>
                <td>
                  <input type="text" name="phoneNumber" value={contactsInput[index].phoneNumber} onChange={handleChange(index)}/>
                </td>
                <td>
                  <input type="text" name="email" value={contactsInput[index].email} onChange={handleChange(index)}/>
                </td>
                <td>
                  <input type="button" value="Save" onClick={handleSave}/>
                  <input type="button" value="Cancel" onClick={handleCancel}/>
                </td>
              </tr>
            ) : (
              <tr>
                <td>{contact.fullName}</td>
                <td>{contact.address}</td>
                <td>{contact.phoneNumber}</td>
                <td>{contact.email}</td>
                <td>
                  <input type="button" value="Edit" onClick={() => setCheckId(contact.id)}/>
                </td>
              </tr>
            )}
          </Fragment>
        ))}
        </tbody>
      </table>
    </div>
  )

}

Answer the question

In order to leave comments, you need to log in

2 answer(s)
I
Ivan Ivanovich, 2021-11-22
@andrey_chirkin

const copyArray = (array = []) => JSON.parse(JSON.stringify(array));

const [contacts, setContacts] = useState(copyArray(data));
const [contactsInput, setContactsInput] = useState(copyArray(data));

[...data] is array unrolling. In this case, it's useless because it's an array of non-primitives. Objects are copied by reference, not by value.
If the array had values ​​in the form of primitives, this would work.
I suggested the easiest way, but in general it is not optimal in terms of performance. A better way to do this is with the cloneDeep method from the lodash library.

A
Alexandroppolus, 2021-11-22
@Alexandroppolus

Inside handleChange change the line a little:

contactsInputCopy[index] = { ...contactsInputCopy[index], [name]: value }

so instead of changing the object, you create a new one

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question