Logic of updating an object in react with useState

Viewed 53

I have a component renderRoyaltyAccount, that gets rendered x number of times depending on the input that sets royaltyAccount.

In this component I have 2 fields, one for the name of the account, and the second a percentage.

What I wanted to do is depending of the number of accounts to create, create an object with those two fields for each, example :

If he chooses to create two accounts , to have a the end (what I thought but could be not the best choice :) ) :

{
    1: {
      "account": "test1",
      "percentage": 2,
    },
    2: {
      "account": "test@",
      "percentage": 0.5
    }
}

I tried with a useState and updating it with onChange with inputs, but it was a mess LOL.

If anyone could help me with this state, and specially the logic with objects and hooks. Thank you

export default function FormApp() {

    const [royaltyAccount, setRoyaltyAccount] = useState(1);

    const [allAccounts, setAllAccounts] = useState ({
            
                {
                    "account": "",
                    "percentage": 1,
                },
            
        })

    const renderRoyaltyAccounts = () => {
        let items = [];
        for (let i = 0; i < royaltyAccount; i++) {

            items.push(
                <div key={i}>
                    <div>
                        <label>Royalty Account n° {i + 1}</label>
                        <input onChange={()=> setAllAccounts(???)} type="text"/>
                    </div>
                    <div>
                        <label>Royalty %</label>
                        <input onChange={()=> setAllAccounts(???)} type="text"/>
                    </div>
                </div>
            )
        }
        return items;
    }


return (

  <>
    <label> Royalty account(s)</label>
    <input onChange={(e) => { setRoyaltyAccount(e.target.value)}} type="number"/>
    {
      renderRoyaltyAccounts()
     }

  </>

)


}

1 Answers
  1. Dynamically compute the allAccounts state array from the initial royaltyAccount state value. Add an id property to act as a GUID for each account object.
  2. Create a handleRoyaltyAccountChange onChange handler to either append a computed diff of the current allAccounts array length to the new count value, or to slice up to the new count if less.
  3. Create a handleAccountUpdate onChange handler to shallow copy the allAccounts state array and update the specifically matching account object by id.
  4. Give the inputs a name attributeand pass the mappedallAccountselement object's property as thevalue` prop.

Code:

import { useState } from "react";
import { nanoid } from "nanoid";

function FormApp() {
  const [royaltyAccount, setRoyaltyAccount] = useState(1);

  const [allAccounts, setAllAccounts] = useState(
    Array.from({ length: royaltyAccount }).map(() => ({
      id: nanoid(),
      account: "",
      percentage: 1
    }))
  );

  const handleRoyaltyAccountChange = (e) => {
    const { value } = e.target;
    const newCount = Number(value);
    setRoyaltyAccount(newCount);
    setAllAccounts((accounts) => {
      if (newCount > accounts.length) {
        return accounts.concat(
          ...Array.from({ length: newCount - accounts.length }).map(() => ({
            id: nanoid(),
            account: "",
            percentage: 1
          }))
        );
      } else {
        return accounts.slice(0, newCount);
      }
    });
  };

  const handleAccountUpdate = (id) => (e) => {
    const { name, value } = e.target;
    setAllAccounts((accounts) =>
      accounts.map((account) =>
        account.id === id
          ? {
              ...account,
              [name]: value
            }
          : account
      )
    );
  };

  return (
    <>
      <label> Royalty account(s)</label>
      <input
        type="number"
        onChange={handleRoyaltyAccountChange}
        value={royaltyAccount}
      />
      <hr />
      {allAccounts.map((account, i) => (
        <div key={account.id}>
          <div>
            <div>Account: {account.id}</div>
            <label>
              Royalty Account n° {i + 1}
              <input
                type="text"
                name="account"
                onChange={handleAccountUpdate(account.id)}
                value={account.account}
              />
            </label>
          </div>
          <div>
            <label>
              Royalty %
              <input
                type="text"
                name="percentage"
                onChange={handleAccountUpdate(account.id)}
                value={account.percentage}
              />
            </label>
          </div>
        </div>
      ))}
    </>
  );
}

Edit logic-of-updating-an-object-in-react-with-usestate

Related