Kibo
Kibo

Reputation: 7

"TypeError: Cannot read properties of undefined (reading 'id')"

I'm building the website but I got error in the browser's console, what's the problem and how to fix it. And another promblem is I can not get value from first and second select but input is fine.

Here's the code.

const Refund = () => {
  const [payment, setPayment] = useState({
    rpayment: ""
  });

  const [reason, setReason] = useState({
    rrefund: ""
  });

  const [date, setDate] = useState({
    ddate: ""
  });

  const handleInputOnChange = ({ currentTarget: select, input}) => {
    const tempPayment = {...payment};
    tempPayment[select.id] = select.value;
    setPayment(tempPayment);
    console.log(tempPayment)

    const tempReason = {...reason};
    tempReason[select.id] = select.value;
    setReason(tempReason);
    console.log(tempReason)

    const tempDate = {...date};
    tempDate[input.id] = input.value;
    setDate(tempDate);
    console.log(tempDate)
  };

  return (
    <div className="refund">
      
    <fieldset className="refund__container">
        <legend align="center">
        </legend>
        <form className="refund__form">
          <div className="refund__form__container">
            <div className="refund__form__item" >
              <select name="rpayment" id="rpayment" value={payment["rpayment"]} onChange={handleInputOnChange}>
                <option value="none" selected disabled hidden>ช่องทางการรับเงิน</option>
                <option value={payment["rpayment"]}>Credit Card</option>
                <option value={payment["rpayment"]}>Paypal</option>
                <option value={payment["rpayment"]}>Wallet</option>
              </select>
            </div>
            <div className="refund__form__item">
                <input
                 type="date" 
                 id="rdate" 
                 name="rdate"
                 value={reason["ddate"]}
                 onChange={handleInputOnChange}
                 />
            </div>
            <div className="refund__form__item">
              <select name="reason" id="reason" value={reason["rrefund"]} onChange={handleInputOnChange}>
                <option value="none" selected disabled hidden>เหตุผลที่ต้องการเงินคืน</option>
                <option value={reason["rrefund"]}>I need to cancle</option>
                <option value={reason["rrefund"]}>It's not comfortable</option>
                <option value={reason["rrefund"]}>Too expensive</option>
              </select>
            </div>
          </div>
        </form>
        <input type="submit" className="refund__btn" value="Submit" />
      </fieldset>
  </div>
  )
}

export default Refund;

Thank you in advance!!

Upvotes: 0

Views: 4201

Answers (1)

lazy.lizard
lazy.lizard

Reputation: 919

There's such a huge amount of problems that I don't even know which one to start from.

Well let's start with state. One state is enough.

const [formState, setFormState] = useState({
 rpayment: "none", // refers to paymentMethods items
 rrefund: "expensive", // refers to refundReasons value field
 ddate: "" // here's input string
})

Second - selects and their change handlers.

// simple approach
const paymentMethods = ['none', 'Credit', 'Paypal', 'Wallet'];

// complex approach
const refundReasons = [
 {
   title: 'I need to cancle',
   value: 'need'
 },
 {
   title: 'Too expensive',
   value: 'expensive'
 }
 // add some more
]

Select handler. Here we are currying function and returning a new one with enclosed stateFieldName referring to our composed state field. But I'm not actually sure if we must use event.target or event.currentTarget - try both, one will work for sure.

const selectHandler = (stateFieldName) => (event) => {
 setFormState({
   ...formState,
   [stateFieldName]: event.currentTarget.value

 })
}

Render selects (no styles, sorry)

// no need to apply hidden fields
<select
  onChange={selectHandler('rpayment')} // yes, this is a function call, it returns a new function
>
{paymentMethods.map((item) => (
  <option
    key={item}
    value={item}
    selected={item === formState.rpayment}
  >
   {item}
  </option>
))
}
</select>

// complex select

<select
  onChange={selectHandler('rrefund')}
>
{refundReasons.map(({title, value}) => (
  <option
    key={value}
    value={value}
    selected={value === formState.rrefund}
  >
   {title}
  </option>
))}
</select>


// input
<input
  type="date" 
  value={formState.ddate}
  onChange={selectHandler(ddate)} // probably this handler will work here too
/>

And finally submit button. If your form is supposed to send async request, your submit button doesn't need value prop and any handler. I general it must have type='submit' prop but the form itself must have a submit hander and send data stored in your state.

Something like

<form
  onSubmit={() => {
    axios.post('/some/api', {
      data: formState
    })
    .then(/* you logic here */)
  }}
>
// your inputs here
</form>

Sorry for approximations and possible mistakes, I hope you got the main idea.

Upvotes: 4

Related Questions