0

I'm getting an infinite loop in the following and can't figure out why.

In one file I have the following declared. Essentially what I'm trying to achieve is to return a stateful errors response which will be updated by revalidating value only when value changes against two schemas (FYI i'm using YUP but that's irrelevant)

export const myValidation = (validationSchema: any, requiredSchema: any, value: any) => {
    const [errors,setErrors] = useState([{}]);
    useEffect(() => {
            Promise.all([
                validationSchema.validate(value, { abortEarly: false }),
                requiredSchema.validate(value, { abortEarly: false })])
                .catch(error => {
                    let _errors = error.inner.map((err: any) => {
                        let path = err.path;
                        let message = err.message;
                        return { [path]: message };
                    });
                    setErrors(_errors);
                });
      },[value]); 

    return Object.assign({}, ...errors);

}

I'm referencing this from within my react component as follows:

    const errors =  myValidation(requiredSchema,validationSchema,{
    title: titleValue, 
    description: descriptionValue
});

titleValue and descriptionValue are both props on the component.

The component itself looks like this:

export default function TitleDescriptionSection({titleValue, descriptionValue, onChange, disabled}: Props) {

const errors =  myValidation(requiredSchema,validationSchema,{
    title: titleValue, 
    description: descriptionValue
});
console.log(errors);
return (
    <Card><Card.Section>
        <FormLayout>
            <TextField type="text" label="Title" value={titleValue}
                       onChange={(value) => onChange({title: value, description: descriptionValue})}
                       disabled={disabled} error={errors ? errors.title : null}/>
            <RichTextField label='Description' value={descriptionValue}
                           onChange={(value: string) => onChange({title: titleValue, description: value})}
                           disabled={disabled} error={errors ? errors.description : null}/>
        </FormLayout>
    </Card.Section>
    </Card>

);

}

When the field values change they call onChange which causes the parent to re-render and pass the values for those fields back as props where the are run through myValidation again.

This all works fine until the myValidation catch is triggered and setErrors is called, sending it into an infinite loop. For example there is a validation condition which determines the "title" field to be invalid if it is an empty string.

I believe that the problem is that value (in myValidation) on the re-render is being treated as "changed" on each re-render even though the contents of value hasn't changed from the previous render. I just can't seem to wrap my head around why.

1 Answer 1

1

Probably what is happening is that you are creating a new object at each render (with the {}) and passing it as argument to the myValidation function. Your error set probably triggers a render on the parent component, which re-renders and recreates the value. The value is considered to be changed because you are creating a new object. If you memoize the object replacing:

{
    title: titleValue, 
    description: descriptionValue
}

with a memoized version like

React.useMemo({
    title: titleValue, 
    description: descriptionValue
}, [titleValue, descriptionValue])

The object will only be created if the values of titleValue or descriptionValue changes, and the infinite loop will not happen

The final code for the function invocation would look like this

const errors =  myValidation(requiredSchema,validationSchema,
    React.useMemo({
        title: titleValue, 
        description: descriptionValue
    }, [titleValue, descriptionValue])
);
Sign up to request clarification or add additional context in comments.

Comments

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.