In my react app, I have two address forms on one page that have two save address functions which save the address in the database. There's also one submit button that submits both two fields and navigates to the next page (The plus button in the circle adds the address to the saved addresses list):
What I want to do is to validate the form fields and I'm already doing that by calling two instances of the useForm()
hook:
// useForm hook
const {
control: senderControl,
handleSubmit: handleSenderSubmit,
setValue: senderSetValue,
} = useForm({
defaultValues: {
senderName: '',
senderLastName: '',
senderPostalCode: '',
senderPhone: '',
senderAddress: '',
},
});
// useForm hook
const {
control: receiverControl,
handleSubmit: handleReceiverSubmit,
setValue: receiverSetValue,
} = useForm({
defaultValues: {
receiverName: '',
receiverLastName: '',
receiverPhone: '',
receiverPostalCode: '',
receiverAddress: '',
},
});
I've then added the handleSubmit method of these two hooks to the onClick (onPress
in RN) of the plus button for each field respectively.
This does indeed validate the forms individually but the problem arises when I'm trying to submit the whole page with the SUBMIT button.
I still want to be able to validate both of these two address fields when pressing the general SUBMIT button but I have no idea how I can validate these two instances with one handleSubmit
and get the return data
of both fields' values.
EDIT (CustomInput.js):
const CustomInput = ({
control,
name,
rules = {},
placeholder,
secureTextEntry,
keyboardType,
maxLength,
textContentType,
customStyle,
}) => (
<Controller
control={control}
name={name}
rules={rules}
render={({field: {onChange, onBlur, value}, fieldState: {error}}) => (
<View style={customStyle || styles.container}>
<TextInput
value={value}
onBlur={onBlur}
onChangeText={onChange}
placeholder={placeholder}
keyboardType={keyboardType}
maxLength={maxLength}
textContentType={textContentType}
secureTextEntry={secureTextEntry}
style={[
styles.textInput,
{
borderColor: !error
? GENERAL_COLORS.inputBorder
: GENERAL_COLORS.error,
},
]}
/>
{error && <Text style={styles.errMsg}>{error.message || 'error'}</Text>}
</View>
)}
/>
);
Usage:
<CustomInput
control={control}
name="senderPostalCode"
rules={{
required: 'Postal Code is Required',
}}
placeholder="Postal Code"
keyboardType="number-pad"
textContentType="postalCode"
customStyle={{
width: '49%',
marginBottom: hp(1),
}}
/>
Is there even any way this can be possible at all?
CodePudding user response:
Thanks to @TalgatSaribayev 's comment for leading me to this solution.
I didn't need to set any specific validation rules for the address field and in the end I separated the sender and receiver forms into two different pages.
First, I've got to point out that instead of getting the input values of postalCode
and address
fields with the getValues
API, I used the useWatch
hook to get the most updated values.
// Watch inputs
const watchedInputs = useWatch({
control,
name: ['senderPostalCode', 'senderAddress'],
});
When I saved the input values with getValues
in a variable, I got the previous state instead of the most recent one and the only way to solve that was calling getValues('INPUT_NAME')
whenever I wanted to get the most recent one. (In the end I resolved to use useWatch
).
///////////////////////////////////////////////////////////////////////////////////////////////////
As @TalgatSaribayev pointed out, creating just one useForm
instance was sufficient enough. All I had to do was to create a function which would set the errors manually and check their validation upon pressing the save address button.
// Check if sender postal code input has error
const senderHasError = () => {
if (!/^\d $/.test(watchedInputs[0])) {
setError('senderPostalCode', {
type: 'pattern',
message: 'Postal Code must be a number',
});
return true;
}
// Any other rules
if (YOUR_OWN_RULE) {
setError('senderPostalCode', {
type: 'custom',
message: 'CUSTOM_MESSAGE',
});
return true;
}
// Clear error and return false
clearErrors('senderPostalCode');
return false;
};
The problem was that the errors wouldn't get updated (cleared) even when they had passed the validation. As if the invalid input wouldn't attach onChange
event listeners to re-validate it. Something that happens as default when you submit the form with the onSubmit
mode of useForm
. (https://react-hook-form.com/api/useform)
So I resolved to use the trigger
API of useForm
to manually trigger form validation and listen for the changes on the postalCode
field when the save address button is pressed.
First I created a toggle state which changes the trigger state:
// Postal code onSubmit event state
const [triggered, setTriggered] = useState(false);
Then used the trigger
API of useForm
in a useMemo
to trigger the input validation only if the triggered
state is set to true and the input field's value has changed:
// Manually trigger input validation if postal code's onSubmit event is true
useMemo(() => {
if (triggered) trigger('senderPostalCode');
}, [watchedInputs[0]]);
I assume the way I triggered the input field with the trigger
API works the same way as useForm
's mode of onSubmit
does it under the hood: Starting the trigger when the user presses the save address button by changing the trigger
state with setTrigger
.
// Add address to sender favorites
const handleAddSenderFavAddress = () => {
// Trigger on the press event
setTriggered(true);
// Return if sender postal code input has error
if (senderHasError()) return;
// SAVE ADDRESS LOGIC
///////////////////////////////
};
This was the only way I managed to validate separate input fields apart from the general validation that occurs with useForm
's handleSubmit
function.
I welcome any more answers that might lead to a better solution.
CodePudding user response:
You can use a single instance of useForm hook and register all your fields in both the forms using the same register method.
This way whenever you click on submit, it will fetch the values from all the fields registered in different forms using the same register method.
Have attached a code sandbox link for your reference