usestate in react for setting state

Viewed 83

Hi in my application we are using initialstate where the application sample data will be defined and using the context for state management, below is my sample initial state:

-

creditCard: {
           isSaved: false,
           lastFourDigits: "1235",
           loading: false,
           cardholder: "",
           cardnumber: "",
           cardmonth: "",
           cardyear: "",
           cardcvv: "",
         },
       etc:{}....

and in my component i am using usestate for setting the data as below :

 const { state,actionsCollection } = useContext(StateContext);
  const [cardholder, setcardholder] = useState("");
  const [cardnumber, setcardnumber] = useState("");
  const [cardmonth, setcardmonth] = useState("");
  const [cardyear, setcardyear] = useState("");
  const [cardcvv, setcardcvv] = useState("");

and in onchange i am setting state as below:

<TextField
                    name="cardmonth"
                    label="MM"
                    error={errors.cardmonth}
                    value={cardmonth}
                    onChange={onChange}
                    onBlur={validateInput}
                    helperText={errors.cardmonth && "Invalid month"}
                    className={classes.expiryDateInputs}
                  />
onchange=()=>{
  let cardMonth = /^0[1-9]|1[0-2]/.test(e.target.value);
        if (cardMonth === true) {
          setcardmonth(e.target.value.replace(/\D/g, "").slice(0, 2));
          setErrors({ ...errors, cardmonth: false });
        } else {
          setErrors({ ...errors, cardmonth: true });
        }
        if (state.creditCard.cardyear !== "") {
          validateExpiryDate();
        }

}

passing states to non related components using below code:

const validateForm = () => {
    return actionsCollection.booking.validateForm(
      errors,
      setErrors,
      cardholder,
      setcardholder,
      cardnumber,
      setcardnumber,
      cardType,
      setCardType,
      cardmonth,
      setcardmonth,
      cardyear,
      setcardyear,
      cardcvv,
      setcardcvv,
      isCurrentCaseIncluded,
      cardYearValue
    );
  };

and in actions i am using this code:

const validateForm = (
    errors,
    setErrors,
    cardholder,
    setcardholder,
    cardnumber,
    setcardnumber,
    cardType,
    setCardType,
    cardmonth,
    setcardmonth,
    cardyear,
    setcardyear,
    cardcvv,
    setcardcvv,
    isCurrentCaseIncluded,
    cardYearValue
  ) => {
some validation logic.....
}

is this the correct way what i am doing, can anyone please tell me what i am doing in onchange and in html code is correct or not..

2 Answers

If you need to validate your input inside onChange this code is perfectly fine. But ,It's not a good idea to validate input on the onChange event . Put your validation logic inside onBlur or onSubmit events and in onChange event , simply set state to event value. also, I would recommend better naming convention.

onChange={(e)=>handleOnChange(e)}

///////////

handleOnChange=(event)=>{
          setcardmonth(event.target.value);
}

// move your validation logic to onBlur or onSubmit

Seems like you have defined your initialState up on the context/global level, but at the same time redefined it in one of your local component with a bunch more individual state:

  const { state } = useContext(StateContext);
  const [cardholder, setcardholder] = useState("");
  const [cardnumber, setcardnumber] = useState("");
  const [cardmonth, setcardmonth] = useState("");
  const [cardyear, setcardyear] = useState("");
  const [cardcvv, setcardcvv] = useState("");

That effectly makes your onChange function only update the state you redefined in your component, and it does not affect your context state, and won't update UI if the UI is dependent on context state.:

onchange=()=>{
  let cardMonth = /^0[1-9]|1[0-2]/.test(e.target.value);
        if (cardMonth === true) {
          setcardmonth(e.target.value.replace(/\D/g, "").slice(0, 2));
          setErrors({ ...errors, cardmonth: false });
        } else {
          setErrors({ ...errors, cardmonth: true });
        }
        if (state.creditCard.cardyear !== "") {
          validateExpiryDate();
        }

Meaning you are doing some extra unnecessary work by simplying redefinning things/states.

You can do one of the three ways:

  1. Go straight for local state without global state, and if some child components needs them, you can just pass props and drill them down;
  2. Or combine global and local state: for those states that need to be shared between different non-related components, you define it in global level, ie. your initialstate, for those that doesn't, put them in a local state;
  3. Or go straight for global state.

On the straight global state approach, you can combine useReducer with context, and that makes updating state more managable(considering your initialstate is relatively complex), otherwise, you can pass setState function:

const { state, setState } = useContext(StateContext); // <- pass down setState from context as well
// no need for local states

// this is how to use state value from context:
<TextField
    name="cardmonth"
    label="MM"
    error={errors.cardmonth}
    value={state.cardmonth} // <- access state value using dot notation
    onChange={onChange}
    onBlur={validateInput}
    helperText={errors.cardmonth && "Invalid month"}
    className={classes.expiryDateInputs}
/>

// this is how to update value from context:
onchange=()=>{
  let cardMonth = /^0[1-9]|1[0-2]/.test(e.target.value);
        if (cardMonth === true) {
          setState(); // <- your update logic
        } else {
          setState(); // <- your update logic
        }
        if (state.creditCard.cardyear !== "") {
          validateExpiryDate();
        }
Related