Hello everyone I tried getting react-hook-form to work with KotlinJS using Dukat and Wrappers, this is what I got.
(the wrapper works fine, tried logging it in the console and it indeed is useForm from the library)
This is how I try to use the useForm
hook
val useForm = useForm<ProfileModel>()
val register = useForm.register
val errors = useForm.formState.errors
val handleSubmit = useForm.handleSubmit
val registerInstagram = register("instagram", object : validateOptions<ProfileModel, Any> {
override var required = true
override var maxLength = 40
})
form(classes = "") {
attrs.onSubmit = {
it.preventDefault()
console.log("Submit")
handleSubmit({ data, ev ->
console.log("On Submit " + data)
}, { errors, ev ->
console.log("On submit error " + errors)
})
}
input(type = InputType.text,
classes = "$defaultInputStyle mb-4") {
attrs.placeholder = "Name"
register("name", object : validateOptions<ProfileModel, Any> {
override var required = true
override var maxLength = 15
})
}
input(type = InputType.text,
classes = "$defaultInputStyle mb-4") {
this.ref = registerInstagram.ref
attrs {
this.placeholder = "Instagram"
this.onChange = registerInstagram.onChange
this.onBlur = registerInstagram.onBlur
this.name = registerInstagram.name
}
}
button(classes = defaultButtonStyle, type = ButtonType.submit) {
+"Submit"
}
}
}
useForm indeed does work and it gives me all of the right values like register, errors and such but I am unable to properly connect it to the Input element in Kotlinjs.
Register returns the following, I edited the interface to fit into the attributes of React DOM, as in regular react form hook you'd use <Input (...register)
which would just apply all of these 4 things to the element.
external interface UseFormRegisterReturn {
var onChange: ChangeEventHandler<*> // I changed this
var onBlur: FocusEventHandler<*> // I changed this
var ref: RefCallback<*> //I changed this
var name: InternalFieldName
}
When I click submit I get the submit printed but the handleSubmit from the useForm
isn't ran and no data is getting printed. Not sure what else can I do with this setup.
Here's the rest of the relevant Wrapper code
@JsName("useForm")
external fun <TFieldValues : FieldValues> useForm(props: `T$23`<TFieldValues> = definedExternally): UseFormReturn<TFieldValues, Any>
external interface UseFormReturn<TFieldValues : FieldValues, TContext : Any?> {
var watch: UseFormWatch<TFieldValues>
var getValues: UseFormGetValues<TFieldValues>
var setError: UseFormSetError<TFieldValues>
var clearErrors: UseFormClearErrors<TFieldValues>
var setValue: UseFormSetValue<TFieldValues>
var trigger: UseFormTrigger<TFieldValues>
var formState: FormState<TFieldValues>
var reset: UseFormReset<TFieldValues>
var handleSubmit: UseFormHandleSubmit<TFieldValues>
var unregister: UseFormUnregister<TFieldValues>
var control: Control<TFieldValues, TContext>
var register: UseFormRegister<TFieldValues>
var setFocus: UseFormSetFocus<TFieldValues>
}
Basically, I am wondering how can I register the useForm
to my elements.
EDIT: Some things you have to do
div("w-screen h-screen items-center justify-center p-4") {
form(classes = "") {
attrs.onSubmit = {
it.preventDefault() 1)
handleSubmit({ data, ev ->
console.log("On Submit ", data)
}, { errors, ev ->
console.log("On submit error ", errors)
})(it) 2)
}
input(type = InputType.text,
classes = "$defaultInputStyle mb-4") {
this.ref = registerInstagram.ref
attrs { 3)
this.placeholder = "Instagram"
this.onChange = registerInstagram.onChange
this.onBlur = registerInstagram.onBlur
this.name = registerInstagram.name
}
}
button(classes = defaultButtonStyle, type = ButtonType.submit) {
+"Submit"
}
This at least sends the data, but validation and errors aren't still happening
Okay, here's the full answer with working errors and everything, hopefully this will help someone.
val useForm = useForm<ProfileModel>()
val register = useForm.register
val errors = useForm.formState.errors.asDynamic()
val handleSubmit = useForm.handleSubmit
val registerName = register("name", js("{required: 'Name is required', maxLength: {" +
"value: maxNameLength , message:'Max length for a name is ' + maxNameLength}}"))
div("w-screen h-screen items-center justify-center p-4") {
form(classes = "") {
attrs.onSubmit = {
it.preventDefault()
handleSubmit({ data, ev ->
console.log("On Submit ", data)
}, { errors, ev ->
console.log("On submit error ", errors)
})(it)
}
input(type = InputType.text,
classes = "$defaultInputStyle mb-4") {
this.ref = registerName.ref
attrs {
this.placeholder = "Name"
this.onChange = registerName.onChange
this.onBlur = registerName.onBlur
this.name = "name"
}
}
if(jsTypeOf(errors.name) !== "undefined" ) {
val text = errors.name.message.toString()
p("ml-3 text-sm text-red-600 -mt-2 mb-2") {
+text
}
}
input(classes = defaultButtonStyle, type = InputType.submit) {
}
To get errors to work, I had to cast errors to Dynamic, and for error messages I had to check if the error exists in the first place for that field, if it does show the error p
. One issue I encountered is that you cant directly use errors.name.message
but I had to add it to a variable and cast it to a string.
For validation I changed the second parameter of register to dynamic and now I just pass js({whatever options I need})
no autocomplete and such but the JS compiler does check for mistakes while writing and it's pretty simple to write out so shouldnt pose a problem.
EDIT: Only thing currently missing is when using this library with Typescript it will warn you that you're missing some fields in the IDE when using useForm<Type