Search code examples
javascriptreactjstypescriptreact-hooksonchange

How to capture onChange event from a field with object type State


I am unable to capture the onChange event from a Obect which contains multiple object fields in itself.

Here is my useState hook.

    const [train, setTrain] = useState({
    trainNumber: "",
    trainName: "",
    fromStation: "",
    toStation: "",
    departureDateTime: "",
    arrivalDateTime: "",
//unable to capture travelDetails property changes.
    travelDetails: {   
      coachesClass: [
        {
          availableTickets: "",
          travelClass: "",
          fare: "",
        },
      ],
    },
  });

Suppose I want to check the onChange event for travelDetails.coachesClass[0].availableTickets then how to do that?

        <div className="row">
          <label htmlFor="availableTickets">First AC</label>
          <div className="col-4">
            <input
              type="number"
              name="availableTickets"
              className="form-control"
              placeholder="Available Tickets"
              required
              value={train.travelDetails.coachesClass[0].availableTickets}
              onChange={(e) => handleChange(e)}
            />
          </div>

Here is the onChangeHandler

const handleChange = (e) => {
const { name, value } = e.target;
setTrain((prevState) => {
  return {
    ...prevState,
    [name]: value,
  };
});

};

It is working fine for other fields but inside travelDetails it is not capturing changes.


Solution

  • The way you are updating state will need to be adjusted since you are trying to update a property in an array nested within an object. Try something like this:

      const handleChange = (e) => {
        setTrain((prevState) => {
          const newCoachesClass = [...prevState.travelDetails.coachesClass];
          newCoachesClass[0] = {
            ...prevState.travelDetails.coachesClass[0],
            availableTickets: e.target.value
          };
          return {
            ...prevState,
            travelDetails: {
              ...prevState.travelDetails,
              coachesClass: newCoachesClass
            }
          };
        });
      };
    

    Also since your input is of number type, you probably want to initialize availableTickets to a number instead of a string:

    const [train, setTrain] = useState({
        trainNumber: "",
        trainName: "",
        fromStation: "",
        toStation: "",
        departureDateTime: "",
        arrivalDateTime: "",
        travelDetails: {   
          coachesClass: [
            {
              availableTickets: 0,
        ...
    

    Check out this codepen for an example: Edit React-Update-Nested-State-Array