Home > Software engineering >  How can I make the function work with the updated state?
How can I make the function work with the updated state?

Time:09-23

I have a handleValid function to validate a form, and when I click submit, the function is triggered for validation and calls handleSelfValidation, in the handleSelfValidation app they write the form state information and change the state, but handleInfoCheck is looking at the previous state, and for this reason I need to click twice to "Send".

const handleValid = () => {
    members
      .filter((member: Tourist) => {
        return member.createdIn === touristCreatedIn && !member.isEmployee;
      })
      .forEach((member: any, index: any) => {
        personSchema
          .validate(member, { abortEarly: false })
          .then(() => {
            setFieldError({
              [index]: {}
            })
          })
          .catch((errs: any) => {
            setFieldError({})
            errs?.inner?.forEach((err: any) => {
              setFieldError((prev)=> ({
                ...prev,
                [index]: {
                  ...prev[index],
                  [err.path]: err.message,
                },
              }))
            });
          });

        personSchema
          .isValid(member)
          .then((v: any) => {
            console.log('тут', v, index)
            handleSelfValidation(v, index); //isFormValid - true
          })
          .catch((err: any) => {
            // eslint-disable-next-line
            console.error('TouristData YUP isValid Err', err);
          });
      });
    setTimeout(handleInfoCheck);
  };

const handleSelfValidation = (isFormValid: boolean, formIndex: number) => {
    console.log(isFormValid, formIndex, 'test')
    setIsFormsValid((prev) => ({
      ...prev,
      [formIndex]: isFormValid,
    }))
  };

const handleInfoCheck = () => {
    setFirstVisit();

    if (
      Object.values(isFormsValid).every((item: any) => {
        return item === true;
      })
    ) {
      switch (permissionType) {
        case 'tour':
          history.push(`${addTourUrl}/tour-data`);
          break;
        case PERMISSION_TYPE_TRANZIT:
          history.push(`${addTourUrl}/tranzit-data`);
          break;
        default:
          history.push(`${addTourUrl}/tour-data`);
          break;
      }
    }
  };

CodePudding user response:

Issue

The issue here is that React state updates are asynchronously processed, and the state from the current render cycle is closed over in handleValid/handleInfoCheck callback scope.

Solution

Allow the isFormsValid state update to occur in handleSelfValidation and use a useEffect hook with a dependency on isFormsValid to run the additional code.

const handleValid = () => {
  members
    .filter((member: Tourist) => {
      return member.createdIn === touristCreatedIn && !member.isEmployee;
    })
    .forEach((member: any, index: any) => {
      ...

      personSchema
        .isValid(member)
        .then((v: any) => {
          console.log('тут', v, index)
          handleSelfValidation(v, index); // <-- updates state
        })
        .catch((err: any) => {
          // eslint-disable-next-line
          console.error('TouristData YUP isValid Err', err);
        });
    });
};

...

useEffect(() => {
  if (isFormsValid) {
    handleInfoCheck();
  }
}, [handleInfoCheck, isFormsValid]);
  • Related