Сложный setState в React

У меня есть страница со списком заказов, каждый из которых имеет уникальный идентификатор заказа. В каждом разделе заказа на странице есть два текстовых поля: одно для trackingID и одно для несущей. Я хотел бы, чтобы мой объект ввода, который обновляется с помощью функции setInput, «помнил» все заказы, которые были обновлены.

Я написал следующий код, но он перезаписывает входную переменную, если я редактирую текстовое поле, принадлежащее другому идентификатору заказа.

const updateStatus = (e) => {
    const { id, value, name } = e.target;
  
    if (name === "trackingId") {
      setInput((prevState) => ({
        ...prevState,
        orderId: prevState.orderId || id,
        shipData: [{ ...prevState.shipData[0], trackingId: value }],
      }));
    } else if (name === "carrierName") {
      setInput((prevState) => ({
        ...prevState,
        orderId: prevState.orderId || id,
        shipData: [{ ...prevState.shipData[0], carrierName: value }],
      }));
    }
  
    console.info(`inputs are: ${JSON.stringify(input)}`);
  };

Приведенный выше код создает следующий объект после того, как я отредактирую текстовые поля «carrierName» и «trackingId»:

{"orderId":"1","shipData":[{"carrierName":"carrierOne","trackingId":"TrackingOne"}]}

Вместо этого я хотел бы, чтобы код создавал этот объект, если я редактирую текстовые поля CarrierName и trackingId второго orderId, т. Е. Я хочу, чтобы состояние сохраняло предыдущее состояние:

{"orderId":"1","shipData":[{"carrierName":"carrierOne","trackingId":"TrackingOne"}]},{"orderId":"2","shipData":[{"carrierName":"carrierTwo","trackingId":"TrackingTwo"}]}

По какой-то причине я просто не могу заставить его работать, и я был бы очень признателен за помощь того, кто умнее меня!

🤔 А знаете ли вы, что...
JavaScript - это скриптовый язык программирования, разработанный Netscape Communications Corporation.


57
1

Ответ:

Решено

Вы перезаписываете свой ввод с помощью setInput в обоих случаях - состояние, которым вы управляете, является просто объектом, а не массивом, как вы хотите.

Состояние там такое:

{
    orderId: number,
    shipData: Array<{ trackingValue: string, carrierName: string  }>
}

Похоже, что результат, который вы хотите, должен иметь состояние, подобное

Array<{
    orderId: number,
    shipData: Array<{ trackingValue: string, carrierName: string  }>
}>

Позвольте мне предложить быстрое решение - вы, вероятно, захотите индексировать здесь по orderId, а не хранить их в массиве.

Редактировать:

const App = () => {
  const [input, setInput] = React.useState({});

  const updateShipDataForOrder = (orderId, shipData) => {
      setInput((prevState) => {
          // Create new ship data based on the previous data + new entry
          const newShipData = {
              ...prevState[orderId],
              ...shipData,
          };

          // Overwrite that value in our state
          const newState = {
              ...prevState,
              [orderId]: newShipData
          };

          return newState;
      })
  }

  const updateStatus = (e) => {
      const { id, value, name } = e.target;

      updateShipDataForOrder(id, { [name]: value })
  };


  // Convert back to the array you wanted
  const result = Object.entries(input).map(([orderId, shipData]) => {
      return {
          orderId,
          shipData
      }
  })

  return (
      <div>
        <div>
            <h1>Order one</h1>
            Tracking ID: <input id = "1" name = "trackingId" onChange = {updateStatus} />
            Carrier Name: <input id = "1" name = "carrierName" onChange = {updateStatus} />
        </div>
        <div>
            <h1>Order two</h1>
            Status: <input id = "2" name = "trackingId" onChange = {updateStatus} />
            Carrier Name: <input id = "2" name = "carrierName" onChange = {updateStatus} />
        </div>
        <br />
        <div>result: {JSON.stringify(result, null, '\t')}</div>
      </div>
  )
}

const root = document.getElementById('root')

ReactDOM.render(<App />, root)
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

<div id = "root"></div>