Probably being stupid here but I'm struggling to get this working. I'm trying to create a form based off an object. Everything is fine when I'm not using a loop, but when I do, it stops working.
Checkbox.tsx
const Checkbox = React.forwardRef(
({ label, name, value, onChange, defaultChecked, ...rest }:any, forwardedRef:any) => {
const [checked, setChecked] = React.useState(defaultChecked);
React.useEffect(() => {
if (onChange) {
onChange(checked);
}
console.log(checked, 'checked')
console.log(name, 'name')
}, [checked]);
return (
<div onClick={() => setChecked(!checked)} style={{ cursor: "pointer" }}>
<input
style={{ display: "none" }}
ref={forwardedRef}
type="checkbox"
name={name}
value={value}
checked={checked}
onChange={e => {
setChecked(e.target.checked);
}}
/>
[{checked ? "X" : " "}]{label}
</div>
);
}
);
My Test() parent component:
export default function Test() {
const onSubmit = (data:any) => {
alert(JSON.stringify(data));
};
const { handleSubmit, register, errors } = useForm();
return (
<div className="App">
<form onSubmit={handleSubmit(onSubmit)}>
<Checkbox
ref={register({ required: "This is required" })}
name="styles"
value={"tops"}
label=" tops"
/>
<Checkbox
ref={register({ required: "This is required" })}
name="styles"
value={"bottoms"}
label="bottoms"
/>
{errors.styles && <p className="error">{errors.styles.message}</p>}
<h2>Next section</h2>
<Checkbox
ref={register({ required: "Please select a colour" })}
name="colors"
value={"red"}
label=" Color (custom checkbox)"
/>
<Checkbox
ref={register({ required: "Please select a colour" })}
name="colors"
value={"blue"}
label=" Color (custom checkbox)"
/>
{errors.colors && <p className="error">{errors.colors.message}</p>}
<button type="submit">submit</button>
</form>
</div>
);
}
Everything works properly here, my data is structured as follows on submit:
{"styles":["bottoms","tops"],"colors":["blue"]}
My problem arises when I try to build out the form from an object, like so:
'../content/formStyle'
const girl = [
{
name: 'styles',
checkboxes: [
{
value: 'dresses',
label: 'Dresses',
},
{
value: 'pants',
label: 'Pants',
},
{
value: 'skirts',
label: 'Skirts',
},
{
value: 'shorts',
label: 'Shorts',
}
]
},
{
name: 'colors',
checkboxes: [
{
value: 'coral',
label: 'Coral'
},
{
value: 'lime',
label: 'Lime'
},
{
value: 'mint',
label: 'Mint'
},
{
value: 'raspberry',
label: 'Raspberry'
},
{
value: 'red',
label: 'Red'
},
{
value: 'purple',
label: 'Purple',
},
{
value: 'teal',
label: 'Teal'
},
{
value: 'blue',
label: 'Blue'
},
{
value: 'pink',
label: 'Pink'
}
]
}
]
export { girl }
And the component changes to:
import {girl} from '../content/formStyle'
export default function Test() {
const onSubmit = (data:any) => {
alert(JSON.stringify(data));
};
const { handleSubmit, register, errors } = useForm();
return (
<div className="App">
<form onSubmit={handleSubmit(onSubmit)}>
{girl?.map((section, index:any) =>{
return (
<>
<h2 className="font-bold">{section.name}</h2>
{section.checkboxes.map(checkbox => {
return (
<Checkbox
ref={register({ required: "This is required" })}
name={checkbox.value}
value={checkbox.value}
label={checkbox.label}
/>
)
})}
</>
)
})}
<button type="submit">submit</button>
</form>
</div>
);
}
Then I get the error:
react-hook-form.es.js?5302:429 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'ref')
at validateField (react-hook-form.es.js?5302:429:43)
at eval (react-hook-form.es.js?5302:1199:1)
What am I doing wrong?
CodePudding user response:
Changing the register part of <Checkbox />
fixed it:
<Checkbox
key={index}
ref={register()}
name={checkbox.value}
value={true}
label={checkbox.label}
/>
CodePudding user response:
I feel like react-hook-form
makes things much harder whereas you could write your own implementation of FormData
. I'm not able to explain why your code is not working, however I can propose an alternative explained easily here:
import React, {useRef} from 'react';
function App() {
const form = useRef<HTMLFormElement>(null)
const submit = (e:any) => {
e.preventDefault();
const data = new FormData(form.current!);
const name = data.get("name");
console.log("name =", name);
}
return (
<form ref={form} onSubmit={submit}>
<label>
<input type="checkbox" name="name" />
<span>TRUC</span>
</label>
<button type="submit">Envoyer</button>
</form>
);
}
export default App;
Just like react-hook-form
the goal here is to avoid countless states controlling the inputs. The FormData object will collect the values and create an object according to the names.
Note that 'on'
is used as value for a checkbox, not a boolean.
NOTE: printing
data
in the console will show an empty object but the values are here.