react-hook-form: Slow input change when using formState
I just want to report a something strange I’ve seen using the library.
Bug: when clicking / focusing different inputs, I had to wait several seconds before the validation to be triggered and focus to be changed to the new field.
Solution : Strangely what solved this was to put register() calls in an effect (rather than in the fields ref prop).
Tested on a Redmi Android device.
Using : “react-native”: “https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz”, “react-hook-form”: “5.0.3” “expo”: “^37.0.0” “proxy-polyfill”: “^0.3.1” …
The code needed validation with custom error messages, formState, controlled & uncontrolled components (picker, masked input, …).
Below a simplified version of the code. Same code with register calls inside ref props would be slow. When commenting the use of formState, input change would be fast again.
With this complexity, I was not sure what worked and what didn’t but my guess reading other issues was maybe it’s something with Proxy on react native, or maybe a bad use of controlled components…
import React, {useState, useMemo, useEffect} from 'react';
import {View} from 'react-native';
import {HelperText} from 'react-native-paper';
import {useForm} from 'react-hook-form';
import * as yup from 'yup';
import IMask from 'imask';
import Picker from '../../components/Picker';
import TextField from '../../components/TextField';
import Button from '../../components/Button';
import {useAccount} from '../../contexts/Account';
const Form = () => {
const {profile = {}, profileApi} = useAccount();
const [input, setInput] = useState(
profile.input ? profile.input : null
);
const items = useMemo(
() => [
{label: 'none', value: '-'},
{label: 'item1', value: '1'},
{label: 'item2', value: '2'},
],
[]
);
const test1 = (value) => value !== '-';
const test2 = (value) => value !== '!';
const test3 = (value) => value !== '?';
const schema = useMemo(
() =>
yup.object({
picker: yup.string().test({
test: test1,
message: "required",
}),
maskedInput: yup
.string()
.test({test: test2, message: "message1"})
.test({test: test3, message: "message2"}),
}),
[]
);
const {register, setValue, watch, handleSubmit, errors, formState} = useForm({
mode: 'onChange',
defaultValues: {
picker: profile.picker ?? items[0].value
},
validationSchema: schema,
});
useEffect(() => {
register({name: 'picker'});
register({name: 'maskedInput'});
}, []);
const valid = formState.isValid;
const onSubmit = async (data) => {
try {
const result = await API.update(data);
} catch (e) {
console.log({error: e});
}
};
return (
<View>
<Picker
value={watch('picker')}
setValue={(value) => setValue('picker', value, true)}
items={items}
/>
<HelperText type="error">
{errors['picker'].message}
</HelperText>
<TextInput
value={input}
defaultValue={watch('maskedInput')}
setValue={(value) => {
const b = mask.resolve(value);
setValue('maskedInput', b, true);
setInput(b);
}}
/>
<HelperText type="error">
{errors['maskedInput'].message}
</HelperText>
<Button onPress={handleSubmit(onSubmit)} disabled={!valid}>
Submit
</Button>
</View>
);
};
export default Form;
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 22 (13 by maintainers)
Controlleris basically doing the register inside 😦 they r the same thing exceptControlleris a wrapper and does a bit more for you. @vladshcherbinWhat you have above is probably the cheapest in terms of perf.
Controllerlet me know if this make sense.
Yes,
useEffectis the right place to do it, and plus you can useControlleras well. I probably should update some of the examples in the doc.@lapico I have updated the validate schema section to include a react native example 😃 going to close the issue now 😃 feel free to follow up with more questions.
@vladshcherbin imagine when you working with
react-select,muiandandwithControllerit can take care of those registration for you as a wrapper.<Controller as={TextField} name="test" control={control} />Done