0

I'm fairly new to React and I'm trying to get some dynamic content working based on a call to an API to validate some input data.

Because of the way I'm using the yup validation I think the code that handles the response from the API call has to be outside of the react function body. This means that I can't use "setOfferState" as shown in my example below.

I've tried doing it with just a basic variable "offer" and this seems to work OK most of the time, but I'm not convinced this will always work as I sometimes get errors in the console and have to reload the page. I understand that this is not the right way to set variables in React.

Both offer and offerState are shown in the example below, but offerState doesn't work (Error: Invalid hook call. Hooks can only be called inside of the body of a function component.)


let offer = '';
const [offerState, setOfferState] = useState(0);

const schema = yup.object().shape({
  discount: yup.string().max(20).test('custom', null,
    function (value) {
      offer = '';
      setOfferState('');
      return new Promise((resolve, reject) => {
        const api = new ApiFacade();
        api.validateCode(value).then((result) => {
          offer = result.value.offerDescription;
          setOfferState(result.value.offerDescription);
        })
      })
    })
});

const YourDetails = () => {
  const formApi = useForm({
    resolver: yupResolver(schema)
  });

  const { handleSubmit, setValue, trigger } = formApi;

  return (
    <FormProvider {...formApi}>

        <form onSubmit={handleSubmit(onSubmit)}>
          
              <Input name="discount" placeholder="Referral/Discount Code" />          
              <Button type="button" onClick={async () => { const result = await trigger("discount"); }}>Check</Button>
          
          {/* {offer.length > 0 && ( */}
            <Typography>{offer}</Typography>
            <Typography>{offerState}</Typography>
          {/* )} */}
          <StepNav handleNext={handleSubmit(onSubmit)} />
        </form>
    </FormProvider>
  );
};

export default YourDetails;

I would be grateful for any suggestions to resolve the state issue or how to move the validation code or validation response code into the function body.

2
  • Why are you doing all of that stuff inside a yup validator? You also are not rejecting or resolving the promise, so the validation runs forever. Instead of describing the problem you're having, describe what you are trying to accomplish, because the code you are showing won't reliably work, even with minor tweaks. Commented May 26, 2021 at 10:52
  • I'm adding the offer code validation. Yup was already in use for validating other fields so seemed like a good place to do it. Also, I missed of the resolve bits when cutting it down for this post, I've added them back in now. Commented May 26, 2021 at 13:21

1 Answer 1

2

Move your schema into the function body:

const YourDetails = () => {
let offer = '';
const [offerState, setOfferState] = useState(0);

const schema = yup.object().shape({
  discount: yup.string().max(20).test('custom', null,
    function (value) {
      offer = '';
      setOfferState('');
      return new Promise((resolve, reject) => {
        const api = new ApiFacade();
        api.validateCode(value).then((result) => {
          if (result.hasFailed) {
            resolve(this.createError({ message: `Error checking code` }));
          } else {
            if (result.value.errorCode) {
              resolve(this.createError({ message: result.value.errorMessage }));
            } else {
              offer = result.value.offerDescription;
              setOfferState(result.value.offerDescription);
              resolve(true);
            }
          }
      })
    })
});

  const formApi = useForm({
    resolver: yupResolver(schema)
  });

  const { handleSubmit, setValue, trigger } = formApi;

  return (
    <FormProvider {...formApi}>

        <form onSubmit={handleSubmit(onSubmit)}>
          
              <Input name="discount" placeholder="Referral/Discount Code" />          
              <Button type="button" onClick={async () => { const result = await trigger("discount"); }}>Check</Button>
          
          {/* {offer.length > 0 && ( */}
            <Typography>{offer}</Typography>
            <Typography>{offerState}</Typography>
          {/* )} */}
          <StepNav handleNext={handleSubmit(onSubmit)} />
        </form>
    </FormProvider>
  );
};

export default YourDetails;
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for that, so simple! For some reason I thought it needed to be outside it for Yup to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.