I making a project in React that I need to fill a form and then send what was answered into my DB, to do so I was wanting to use the hook useDebounce to send the submition after the user stop the interaction. But when I tried to do so it results in a infinite loop and I don't know how to implement it.
This is my code review:
First I have my hook that have all the operations from that entity using GraphQL:
// useRecommendation.js
...
// other operations
export const useUpdate = () => {
const [updateCriteria, { loading, error }] = useMutation(UPDATE_CRITERIA);
const update = { handle: updateCriteria, loading, error };
return update;
};
Then I import all operations into a object making like a model:
// Recommendation.js
const Recommendation = {
all: useGetAll,
byCategory: useGetByCategory,
create: useCreate,
update: useUpdate
}
export default Recommendation
And then I'm importing this entity in my component handler:
export const useRecommendationFormHandler = ({ form }) => {
const updateRecommendation = Criteria.update();
const submitRecommendation = async () => {
try {
const { data } = await updateRecommendation.handle({
variables: {
input: {
conformity: form.conformity,
importance: form.importance,
comment: form.comment,
conclusion: form.conformity === 0 && form.importance !== null,
},
criteria_id: form.id
}
});
return data;
} catch (error) {
console.error("Erro ao atualizar a criteria", error);
}
};
return {
isLoading: updateRecommendation.loading
}
};
I think it is important to say that the component that I'm trying to use the useDebounce is being rendered within a loop:
<Grid sx={{ paddingX: 3 }}>
{forms.length !== 0 && criterias.data.map((data, i) => (
<RecommendationForm
key={data.id}
recommendation={data.recommendation.description}
form={forms.find(f => f.id === data.id)}
onChange={handleChange}
index={i}
onRefetch={onRefetch}
/>
))
}
</Grid>
What can I do to send the answers to my db using this hook?
Edit: I was using the useDebounce like this:
const debouncedForm = useDebounce(form, 1000);
const updateRecommendation = Criteria.update();
const submitRecommendation = useCallback(async () => {
try {
const { data } = await updateRecommendation.handle({
variables: {
input: {
conformity: debouncedForm.conformity,
importance: debouncedForm.importance,
comment: debouncedForm.comment,
conclusion: debouncedForm.conformity === 0 && debouncedForm.importance !== null,
},
criteria_id: debouncedForm.id
}
});
return data;
} catch (error) {
console.error("Erro ao atualizar a criteria", error);
}
}, [debouncedForm, updateRecommendation]);
useEffect(() => {
if (debouncedForm) {
submitRecommendation()
}
}, [debouncedForm, submitRecommendation])
If I had to guess, it would be this:
const submitRecommendation = // ...
useEffect(() => {
if (debouncedForm) {
submitRecommendation()
}
}, [debouncedForm, submitRecommendation])
I imagine that submitRecommendation
is updating the component state and always returning a new function, which is causing useEffect
to be invoked on every render cycle (which it self-triggers). It looks like submitRecommendation
shouldn't be affecting when this useEffect
should be triggered. If you have the eslint rule on for it, ignore it.
useEffect(() => {
if (debouncedForm) {
submitRecommendation()
}
}, [debouncedForm])