UseState array Hooks return a new object in every change in the input field

I’m using React hooks for a project I’m working on. I’m coming across a situation where I want to use an array in the useState hook, but I’m not really certain on how to do this.

I created a table which changes the number of its columns dynamically, according to the number of days of the month.

I’ve tried something like this:

const [inText, setInText] = useState([]);
const inputText = (e) => {
    setInText([...inText,{
        id:inText.length,
        value:e.target.value
    }]);
}

numberOfDays is a variable which I calculate. It contains the number of days in the current month.

const renderINP = () => {
    let td = [];
    for (let i = 1; i <= numberOfDays; i++) {
        td.push(<td key={i}><input type="text" onChange={inputText} /></td>);
    }
    return td;
};

This is the code which renders the table to the browser:

<table>
    <tbody>
        <tr className="work-days">
            <td>Working days</td>
            {renderINP()}
        </tr>
    </tbody>
</table>

I try to catch the ‘inText’ with cosole.log(inText). When I type something in the input, like 0 for example, it creates an object, {id: 0, value: ‘0’}. When I type again, it creates two objects {id: 0, value: ‘0’} and {id: 1, value: ‘0.’}. Also, when I delete something, it creates another object.

How can I create just one object for every input, and how can I get the value of every inText individually? For example, get the value of inText in the second input, not the value off all objects.

Answer

useState is mostly used for keeping single values. In this case, you have an array of objects and want to update value for individual objects. useReducer is preferable.

You can read more about useReducer

This should do what you are looking for:

import React, { useReducer } from "react";

const App = () => {
  const reducer = (state, action) => {
    if (action.type === "ADD") {
      const newState = [...state];
      newState.push({ id: action.id, value: action.value });
      return newState;
    }

    if (action.type === "UPDATE") {
      const newState = [...state];
      const foundItem = newState.find((item) => item.id === action.id);
      foundItem.value = action.value;
      return newState;
    }
  };

  const initialState = [];

  const [state, dispatch] = useReducer(reducer, initialState);

  const numberOfDays = 7;

  const inputText = ({ id, value }) => {
    if (state.find((item) => item.id === id)) {
      dispatch({ type: "UPDATE", id, value });
    } else {
      dispatch({ type: "ADD", id, value });
    }

    console.log(state);
  };

  const renderINP = () => {
    let td = [];
    for (let i = 1; i <= numberOfDays; i++) {
      td.push(
        <td key={i}>
          <input
            type="text"
            onChange={(e) => inputText({ id: i, value: e.target.value })}
          />
        </td>
      );
    }
    return td;
  };

  return (
    <>
      <table>
        <tbody>
          <tr className="work-days">
            <td>Working days</td>
            {renderINP()}
          </tr>
        </tbody>
      </table>

      {JSON.stringify(state)}
    </>
  );
};

export default App;