In the code below, I have a few textareas where the user should input jsons.
I tried to use and array (well, object with numeric keys, just for the ease from rest operator assignment), and I also wanted to avoid parsing both JSONs at every change of either.
It works well, except that for the first render, I get validation only on the second json. I am pretty sure it comes down to scope, with old values being stored in the useEffect, but I added the callback versions of setErrors and that didn't work.
What am I doing wrong, and what is the right pattern for this?
PS: I intend to move each textarea into individual components of course, but would like to understand what is wrong here first.
const [jsons, setJsons] = useState<{ [k: number]: string }>({ 0: '', 1: '' });
const [containers, setContainers] = useState<{ [k: number]: object }>({ 0: {}, 1: {} });
const [errors, setErrors] = useState<{ [k: number]: string }>({ 0: '', 1: '' });
const useContainerEffect = (index: number) => {
useEffect(() => {
let container = {};
try {
container = JSON.parse(jsons[index]);
setErrors(() => ({ ...errors, [index]: '' }))
} catch (e) {
setErrors(() => ({ ...errors, [index]: VALUE_IS_NOT_JSON }))
}
setContainers(() => ({ ...containers, [index]: container }))
}, [jsons[index]]);
}
useContainerEffect(0);
useContainerEffect(1);
const jsonInputChange = (e: HTMLTextAreaElement, i: number) => {
setJsons({ ...jsons, [i]: e.value })
}
CodePudding user response:
Try using functional form of useState
to set your errors. Also it's a good practice not to use complex dependencies in dependency array, so they can be statically checked:
const useContainerEffect = (index: number) => {
useEffect(() => {
let container = {};
try {
container = JSON.parse(jsons[index]);
setErrors((err) => ({ ...err, [index]: '' }));
} catch (e) {
setErrors((err) => ({ ...err, [index]: 'VALUE_IS_NOT_JSON' }));
}
setContainers(() => ({ ...containers, [index]: container }));
}, [index]);
};