How to wait for useState to finish before setting useRef value?

I have two following form elements that do similar things. Get value using useRef and adding it to an array of elements using useState. First one works perfectly. It sets the value and then removes everything in input element and unfocuses it. However with the second one I have to choose between pushing an empty string or letting the input element retain its value. I'm guessing that is because useState is async and I just got lucky in the first one because it is a faster operation and it executed before input element was reset. What is the correct way to use these two hooks together with reseting input element afterwards.
<form
onSubmit={(e) => {
e.preventDefault();
const newTopCategoryVal = newTopCategory.current;
if (!newTopCategoryVal) return;
setCategories((prev) => [...prev, { name: newTopCategoryVal.value, subFilters: [] }]);
newTopCategoryVal.value = "";
newTopCategoryVal.blur();
}}
onFocus={() => { setActiveCategory(-1); setActiveSubCategory(-1) }}
>
<form
onSubmit={(e) => {
e.preventDefault();
const newTopCategoryVal = newTopCategory.current;
if (!newTopCategoryVal) return;
setCategories((prev) => [...prev, { name: newTopCategoryVal.value, subFilters: [] }]);
newTopCategoryVal.value = "";
newTopCategoryVal.blur();
}}
onFocus={() => { setActiveCategory(-1); setActiveSubCategory(-1) }}
>
<form
onSubmit={(e) => {
e.preventDefault();
const newSubCatInput = newSubCategory.current;
if (!newSubCatInput || newSubCatInput.value.trim().length < 1) return;
setCategories((prev) => {
const oldCategories = Array.from(prev);
const categoryToEdit = oldCategories[activeCategory];
if (!categoryToEdit) return oldCategories;
oldCategories.splice(activeCategory, 1, {
...categoryToEdit,
subFilters: [...categoryToEdit.subFilters, newSubCatInput.value],
});
return oldCategories;
}
);
newSubCatInput.value = "";
newSubCatInput.blur();
}}
>
<form
onSubmit={(e) => {
e.preventDefault();
const newSubCatInput = newSubCategory.current;
if (!newSubCatInput || newSubCatInput.value.trim().length < 1) return;
setCategories((prev) => {
const oldCategories = Array.from(prev);
const categoryToEdit = oldCategories[activeCategory];
if (!categoryToEdit) return oldCategories;
oldCategories.splice(activeCategory, 1, {
...categoryToEdit,
subFilters: [...categoryToEdit.subFilters, newSubCatInput.value],
});
return oldCategories;
}
);
newSubCatInput.value = "";
newSubCatInput.blur();
}}
>
Solution:
Yeah you've got the right idea. newSubCatInput.value is captured by reference in the anonymous function (closure?, lambda? not sure what js calls em) I think the important part would be avoiding the capture of the DOM element. Using a reference to the string itself should work fine. ```Typescript...
Jump to solution
8 Replies
Solution
Vincent Udén
Vincent Udén17mo ago
Yeah you've got the right idea. newSubCatInput.value is captured by reference in the anonymous function (closure?, lambda? not sure what js calls em) I think the important part would be avoiding the capture of the DOM element. Using a reference to the string itself should work fine.
<form
onSubmit={(e) => {
e.preventDefault();
const newSubCatInput = newSubCategory.current;
const inputString = newSubCatInput.value;

if (!newSubCatInput || newSubCatInput.value.trim().length < 1) return;
setCategories((prev) => {
const oldCategories = Array.from(prev);
const categoryToEdit = oldCategories[activeCategory];
if (!categoryToEdit) return oldCategories;
oldCategories.splice(activeCategory, 1, {
...categoryToEdit,
subFilters: [...categoryToEdit.subFilters, inputString],
});
return oldCategories;
}
);
newSubCatInput.value = "";
newSubCatInput.blur();
}}
>
<form
onSubmit={(e) => {
e.preventDefault();
const newSubCatInput = newSubCategory.current;
const inputString = newSubCatInput.value;

if (!newSubCatInput || newSubCatInput.value.trim().length < 1) return;
setCategories((prev) => {
const oldCategories = Array.from(prev);
const categoryToEdit = oldCategories[activeCategory];
if (!categoryToEdit) return oldCategories;
oldCategories.splice(activeCategory, 1, {
...categoryToEdit,
subFilters: [...categoryToEdit.subFilters, inputString],
});
return oldCategories;
}
);
newSubCatInput.value = "";
newSubCatInput.blur();
}}
>
Vincent Udén
Vincent Udén17mo ago
If that doesn't work, try copying the string with const inputString = newSubCatInput.value.slice()
thevalorised
thevalorisedOP17mo ago
That's what I feared. I wished there was a way to avoid copying more memory. Thank you @Vincent !
Vincent Udén
Vincent Udén17mo ago
If you're not using slice you aren't using more memory. You just change which reference you capture into the arrow function
thevalorised
thevalorisedOP17mo ago
I thought js was deep copying strings when we did that. I have no idea where I got that notion. That's good to know
Vincent Udén
Vincent Udén17mo ago
I won't speak with 100% confidence, but I'd expect that reassignment to just be a reference considering how many questions there are online about how to actually get a deep copy lol
epsilon42
epsilon4217mo ago
Are these supposed to be two separate forms, or two inputs in a single form? Not understanding the whole context/problem but wanted to add that you can also add a ref to the form tag, and name your inputs and in your onSubmit you can use it like: formRef.current.subcategory.value formRef.current.reset()
thevalorised
thevalorisedOP17mo ago
They are different forms but that's a neat feature
Want results from more Discord servers?
Add your server