How can i pass render props to children in React functional components? - reactjs

i am using react-final-form I have a Form Component something like this:
export function Form = ({ children, onSubmit, initialValues, ...props }) => {
return (
<FinalForm
initialValues={initialValues}
onSubmit={onSubmit}
render={({ handleSubmit, submitting, pristine, valid }) => (
<form onSubmit={handleSubmit} className="space-y-6" {...props}>
{children}
</form>
)}
/>
)
}
export default Form;
I want to use this form component like this:
<Form>
({ valid, pristine, submitting } => (
<TextInput name="email" label="Email Address" type="email" />
<Button text="Login" disabled={!valid || pristine || submitting} />
)
</Form>
How can i pass the props valid, pristine and submitting to my childs so i can use it for e.g. disable a Button?

Instead of
{children}
Try this,
{
React.Children.map(children, child => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { submitting, pristine, valid });
}
return child;
});
}

Related

Formik form not rendering

Just setting up a basic bonehead form for learning purposes and ... can't seem to render. No errors. Functional component runs. Just nothing to see...
MyForm.tsx
export const MyForm: React.FC = () => {
console.log("MyForm has been called")
return (
<div>
<Formik initialValues={{ firstName: "roberto" }} onSubmit={data => { console.log(data) }}>
{({ values, handleChange, handleSubmit, handleBlur }) => {
<form onSubmit={handleSubmit}>
<TextField value={values.firstName} onChange={handleChange} onBlur={handleBlur} name="firstName" />
<pre>JSON.stringify(values)</pre>
</form>
}}
</Formik>
</div >
)
}
I've imported MyForm properly into App.tsx, and MyForm is currently all I'm returning from App.tsx.
No errors. Just nothin...
I don't think you're returning your form which is why it's not rendering:
export const MyForm: React.FC = () => {
console.log("MyForm has been called")
return (
<div>
<Formik initialValues={{ firstName: "roberto" }} onSubmit={data => { console.log(data) }}>
{({ values, handleChange, handleSubmit, handleBlur }) => (
<form onSubmit={handleSubmit}>
<TextField value={values.firstName} onChange={handleChange} onBlur={handleBlur} name="firstName" />
<pre>JSON.stringify(values)</pre>
</form>
)}
</Formik>
</div >
)
}
Note that I changed the function's curly braces to parens around your <form>. Alternatively, you could leave the curly braces and instead
{({ values, handleChange, handleSubmit, handleBlur }) => {
return (<form>...</form>)
}}

React Formik Mui TextField with custom input loosing focus

I have a Formik form which contains a few Material UI TextField components. All of them works fine, except for one who looses focus when onChange() is executed. It has a PhoneInput (react-phone-number-input) component as a custom input.
Everything works fine (data is being saved, etc.), but I have to keep clicking on the input to continue writing on it.
I have a feeling it has something to do with the ref. When logging inputRef (PhoneInputRef, provided by TextField) it doesn't look like a normal ref.
Formik component (Parent)
{...}
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleClickRegister}>
{(props) => <RegisterForm {...props} />}
</Formik>
{...}
Form (Children)
{...}
const {
values: {
firstName,
lastName,
email,
confirmEmail,
password,
confirmPassword,
phoneNumber,
},
//Formik methods passed as props
errors,
touched,
handleChange,
handleSubmit,
setFieldValue,
setFieldTouched,
} = props;
{...}
//Formik onChange handler (used by every TextField besides the one containing PhoneInput)
const change = (name, e) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
};
//Custom onChange handler for Formik (used by PhoneInput)
const handleOnChange = (name, value) => {
setFieldValue(name, value);
setFieldTouched(name, true, false);
};
{...}
{/*NORMAL TEXTFIELD*/}
<TextField
className={classes.inputField}
id="confirmPassword"
name="confirmPassword"
label={t("header.registerDialog.form.labelConfirmPassword")}
value={confirmPassword}
onChange={change.bind(null, "confirmPassword")}
error={touched.confirmPassword && Boolean(errors.confirmPassword)}
helperText={
touched.confirmPassword && errors.confirmPassword ? (
errors.confirmPassword
) : (
<>
<br />
</>
)
}
/>
{/*CUSTOM INPUT TEXTFIELD*/}
<TextField
className={classes.textFieldPhoneInput}
key="textFieldPhone"
ref={textFieldRef}
id="phoneNumber"
name="phoneNumber"
label={t("header.registerDialog.form.labelPhone")}
InputLabelProps={{
className: phoneNumber
? classes.inputFieldPhoneLabel
: classes.inputFieldPhoneLabelEmpty,
}}
InputProps={{
inputComponent: ({ inputRef, ...rest }) => (
<PhoneInput
{...rest}
key="phoneInput"
ref={inputRef}
international
countryCallingCodeEditable={false}
name="phoneNumber"
defaultCountry="AR"
onChange={(phone) =>
handleOnChange("phoneNumber", phone)
}
labels={phoneLanguage}
value={phoneNumber}
/>
),
}}
error={touched.phoneNumber && Boolean(errors.phoneNumber)}
helperText={
touched.phoneNumber && errors.phoneNumber ? (
errors.phoneNumber
) : (
<>
<br />
</>
)
}
/>
{...}

react-hook -form with SliderComponent input

Im new to react and I am trying to use a SliderComponent in a react Hook form but I cant seem able to fully understand how Controller works.
Here is my SliderComponent using react-input-slider:
export default function SliderComponent(props) {
const { axis, xmax, xmin, xstep, onChange } = props;;
return (
<div>
<Slider
axis={axis}
x={value.x}
xmax={xmax}
xmin={xmin}
xstep={xstep}
onChange={onChange}
/>
</div>
);
}
And here is my form:
export default function BiometricForm() {
const { handleSubmit, control } = useForm();
return (
<div className="registerForm_Container">
<form onSubmit={handleSubmit(onsubmit)}>
<Controller
control={control}
name="test"
render={({ props: { x, axis, xmax, xmin, xstep } }) => (
<SliderComponent
axis={"x"}
xmax={100}
xmin={1}
xstep={1}
value={x}
onChange={(e) => x.onChange(parseInt(e.target.value))}
/>
)}
/>
<button className="registerForm_Container_button" type="submit">
Register
</button>
</form>
</div>
);
}
I think it might be something to do with useState and that I am not able to reach useState of component. I have read that maybe its not necessary , any help? Thank you!
You are absolutely right, if you use RHF you don't need useState because RHF handles the form state for you. The important thing here is to pass an onChange handler to your <SliderComponent />, so that RHF can watch for value changes of your <SliderComponent /> and update the form state. You should also provide a defaultValue for the field, if you don't want it to be undefined if the user doesn't change the slider before submitting.
Also without the useState inside <SliderComponent />, you can also omit the <SliderComponent /> and just use <Slider />.
function BiometricForm() {
const { handleSubmit, control } = useForm();
const onSubmit = (data) => console.log(data);
return (
<div className="registerForm_Container">
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="test"
defaultValue={50}
render={({ field: { value, onChange } }) => (
<Slider
axis={"x"}
xmax={100}
xmin={1}
xstep={1}
onChange={({ x }) => onChange(x)}
x={value}
/>
)}
/>
<input type="submit" />
</form>
</div>
);
}

React final form triggers handleSubmit after the initial render

I've got only Switch component in my react-final-form. It looks like this:
<Form
onSubmit={onSubmit}
initialValues={initialValues}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="booleanValue" component={Switch} onChange={handleSubmit}/> //triggers when receives value
</form>
)
}
/>
I want to trigger handleSubmit only after user changes, not at first render of the form.
<Field/> doesn't have an onChange prop like you are attempting. Something like this could work.
import { OnChange } from 'react-final-form-listeners'
...
<Form
onSubmit={onSubmit}
initialValues={initialValues}
render={({ handleSubmit, form }) => (
<form onSubmit={handleSubmit}>
<Field name="booleanValue" component={Switch}/>
<OnChange name="booleanValue">
{(value, previousValue) => {
form.submit()
}}
</OnChange>
</form>
)
}
/>
P.S. I hope your Switch component knows to get its value and onChange from the input prop.
Hope that helps!

React-bootstrap switch not displaying

I'm trying to incorporate Formik and React-bootstrap.
It's going as expected except the switch component.
Here is my function for rendering the switch:
function ReactSwitch(props){
return(
<>
<Col md={props.col}>
<Form.Group>
<Form.Check
type="switch"
id={props.id}
name={props.name}
label={props.label}
checked={props.checked}
onChange={props.onChange}
/>
</Form.Group>
</Col>
</>
);
}
And here is my initialization for Formik:
const formik = useFormik({
initialValues: {
wflow_switch: false
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
Note that when I change the type from switch to checkbox, it displays a checkbox but still no label. What am I doing wrong? I'm still learning React so any comments are appreciated.
I guess you'll need to use state and enable enablereinitialize
Try this:
export default function FormSwitch() {
// add checked state
const [checked, setChecked] = useState(false)
const { handleSubmit, values } = useFormik({
initialValues: {
// initial value is set 'false' by state
switch: checked
},
// Control whether Formik should reset the form if initialValues changes
enableReinitialize: true,
onSubmit: (values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2))
setSubmitting(false)
}, 400)
}
})
return (
<form className="form" onSubmit={handleSubmit}>
<ReactSwitch
name="switch"
label="Switch"
id="switch"
checked={checked}
onChange={() => setChecked(!checked)}
/>
<button type="submit">
Submit
</button>
</form>
)
}
Edit: Different approach using useField with <Formik>
import { Formik, useField } from "formik"
import { Col, Form } from "react-bootstrap"
const ReactSwitch = ({ ...props }) => {
const [field, form] = useField(props)
return (
<Col md={props.col}>
<Form.Group>
<Form.Check
type="switch"
id={props.id}
checked={field.value.checked}
onChange={() => form.setFieldValue(props.name, !field.value)}
{...field}
{...props}
/>
</Form.Group>
</Col>
)
}
export default function Switch() {
return (
<Formik
initialValues={{
switch: false
}}
onSubmit={values => alert(JSON.stringify(values, null, 2))}
>
{formik => (
<form onSubmit={formik.handleSubmit}>
<ReactSwitch label="Switch" name="switch" id="switch" />
<button type="submit">submit</button>
</form>
)}
</Formik>
)
}

Resources