Reputation: 385
I'm trying to validate an input field for emailaddresses. The actual validation works, the state updates and everything around it works. However, I noticed a strange behaviour once a false email is validated.
NOTE: I changed the name of my states and attributes to make it more generic and readable.
<TextField
label = {emailFieldAttributes.label}
type = {emailFieldAttributes.type}
onChange = { event => {
onChangeHandler(event)
}}
id = {emailFieldAttributes.id}
value = {state.value_email}
InputProps = {{
autoComplete: 'off',
inputMode: 'string'
}}
error = {state.error.value}
ref = {ref}
></TextField>
function onChangeHandler(event)
{
if (event.target.id.includes('cc'))
{
setState({...state, value_cc: event.target.value});
}
else if (event.target.id.includes('body'))
{
setState({...state, value_body: event.target.value});
}
else
{
setState({...state, value_email: event.target.value});
}
if (state.error != initialErrorState
)
{
resetEmailTooltip();
}
}
function resetEmailTooltip()
{
setEmailStateValues(
{
...state,
error: resetErrorState
});
}
In the submit buttons' onClick, I run the validation and it works as it updates the state correctly and shows a tooltip. In fact, the onChange works as it's supposed to except for 1 slight undesired behaviour. Whenever a validation does return false and updates the state.error, a tooltip is shown. The next time a change is done on the input field the first event only triggers resetEmailTooltip();
and does not the update the state value.
Stepwise - current behaviour
Stepwise - desired behaviour
What I don't understand is why it requires a second event to trigger the state update? Do I have something wrong regarding my thought process when it comes to how onChange is fired?
Upvotes: 2
Views: 212
Reputation: 385
I dug a bit more and found that event.target.value was indeed a bit unstable but it wasn't really the cause of my problem. My problem arose from using 2 setStates which can be seen in the code, I should've spotted this sooner but being new to React it took some time to figure it out.
So even though the event actually tries to update the State, the async nature of setState seems to be overwritten by the 2nd setState in the resetEmailTooltip
function. I solved it by changing the onChangeHandler
and resetEmailTooltip
to look like this (Note: I'm not liking the volume of code to handle this, so I will try and rewrite the code later):
function onChangeHandler(event)
{
if (state.error != initialErrorState )
{
resetEmailTooltip(event);
}
else
{
if (event.target.id.includes('cc'))
{
setState({...state, value_cc: event.target.value});
}
else if (event.target.id.includes('body'))
{
setState({...state, value_body: event.target.value});
}
else
{
setState({...state, value_email: event.target.value});
}
}
}
And
function resetEmailTooltip(event)
{
if (event.target.id.includes('cc'))
{
setState(
{
...state,
value_cc: event.target.value,
error: resetErrorState
});
}
else if (event.target.id.includes('body'))
{
setState(
{
...state,
value_body: event.target.value,
error: resetErrorState
});
}
else
{
setState(
{
...state,
value_email: event.target.value,
error: resetErrorState
});
}
}
The short story is that I update the state with event.target.value in both functions and call one setState instead of both. This ensures that the event.target.value is set and also that I don't need to call both setStates. However, this still feels clunky and I think there's a way to shorten this code even more and get rid of the several if
statements and I will look into that.
Update:
Since updating the local state with the same value doesn't cause a re-render, I decided to cut out more code and even remove the resetEmailTooltip(event)
function. I changed my onChangeHandler(event)
to the following piece of code and it works exactly like I want it to and I even got to clean it up a bit. If possible, I will look into having internal conditions in a setState, but I'm not sure if that's allowed. This is the end result for now, hope it helps anyone if they get stuck like I did with the same issue.
function onChangeHandler(event)
{
const resetErrorState = state.initialErrorState;
if (event.target.id.includes('cc'))
{
setState(
{
...state,
value_cc: event.target.value,
error: resetErrorState
}
);
}
else if (event.target.id.includes('body'))
{
setState(
{
...state,
value_body: event.target.value,
error: resetErrorState
}
);
}
else
{
setState(
{
...state,
value_email: event.target.value,
error: resetErrorState
}
);
}
}
Upvotes: 1