I want to use my component like this
<Form.item>
<MyComponent />
</Form.item>
I can't pass the defaultValue to MyComponent and the Form can't get the value from MyComponent when I hit the submit button.
How can I fix this?
For using a default value you can use initialValues from antd's Form together with the in Form.Item defined name.
E.g.:
<Form
{...layout}
name="basic"
initialValues={{
myComponent: "IntialTestValue"
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="MyComponent"
name="myComponent"
To use custom or third party component you need bind your component to a value and onChange.
E.g.:
const MyComponent = ({ value = {}, onChange }) => {
const [inputValue, setInputValue] = useState(value);
const onInputChange = e => {
setInputValue(e.target.value);
onChange(e.target.value);
};
return (
<>
<Input defaultValue={value} value={inputValue} onChange={onInputChange} />
</>
);
};
Here is a working CodeSandbox.
Another good example can be found int antd Form docs: CodeSandbox
Related
I am new to react and I have just started using Formik
I like how simple it makes making forms and handling forms in react.
I have created multiple custom fields using formik, I am putting the react-select field I created as an example here.
import { ErrorMessage, Field } from "formik";
import React from "react";
import Select from 'react-select'
const SelectInput = (props) => {
const { label, name, id,options, required, ...rest } = props;
const defaultOptions = [
{label : `Select ${label}`,value : ''}
]
const selectedOptions = options ? [...defaultOptions,...options] : defaultOptions
return (
<div className="mt-3">
<label htmlFor={id ? id : name}>
{label} {required && <span className="text-rose-500">*</span>}
</label>
<Field
// className="w-full"
name={name}
id={id ? id : name}
>
{(props) => {
return (
<Select
options={selectedOptions}
onChange={(val) => {
props.form.setFieldValue(name, val ? val.value : null);
}}
onClick = {(e)=>{e.stopPropagation}}
{...rest}
// I want someting like onReset here
></Select>
);
}}
</Field>
<ErrorMessage
name={name}
component="div"
className="text-xs mt-1 text-rose-500"
/>
</div>
);
};
export default SelectInput;
This is the usual code I use for submitting form as you can see I am using resetForm() method that is provided by formik, I want to attach the reseting logic in on submit method itself.
const onSubmit = async (values, onSubmitProps) => {
try {
//send request to api
onSubmitProps.resetForm()
} catch (error) {
console.log(error.response.data);
}
};
If you want to reset the selected value after the form is submitted, you need to provide a controlled value for the Select component.
The Formik Field component provides the value in the props object, so you can use it.
For example:
SelectInput.js
import { ErrorMessage, Field } from 'formik';
import React from 'react';
import Select from 'react-select';
const SelectInput = ({ label, name, id, options, required, ...rest }) => {
const defaultOptions = [{ label: `Select ${label}`, value: '' }];
const selectedOptions = options ? [...defaultOptions, ...options] : defaultOptions;
return (
<div className='mt-3'>
<label htmlFor={id ? id : name}>
{label} {required && <span className='text-rose-500'>*</span>}
</label>
<Field
// className="w-full"
name={name}
id={id ? id : name}
>
{({
field: { value },
form: { setFieldValue },
}) => {
return (
<Select
{...rest}
options={selectedOptions}
onChange={(val) => setFieldValue(name, val ? val : null)}
onClick={(e) => e.stopPropagation()}
value={value}
/>
);
}}
</Field>
<ErrorMessage name={name} component='div' className='text-xs mt-1 text-rose-500' />
</div>
);
};
export default SelectInput;
and Form.js
import { Formik, Form } from 'formik';
import SelectInput from './SelectInput';
function App() {
return (
<Formik
initialValues={{
firstName: '',
}}
onSubmit={async (values, { resetForm }) => {
console.log({ values });
resetForm();
}}
>
<Form>
<SelectInput
name='firstName'
label='First Name'
options={[{ label: 'Sam', value: 'Sam' }]}
/>
<button type='submit'>Submit</button>
</Form>
</Formik>
);
}
export default App;
Therefore, if you click the Submit button, value in the Select component will be reset.
You can also make a useRef hook to the Fromik component and then reset the form within the reset function without adding it as a parameter to the function.
https://www.w3schools.com/react/react_useref.asp
It's one of the really nice hooks you'll learn as you progress through React :)
So if I understood you correctly you want to reset a specif field value onSubmit rather than resetting the whole form, that's exactly what you can achieve using actions.resetForm().
Note: If nextState is specified, Formik will set nextState.values as the new "initial state" and use the related values of nextState to update the form's initialValues as well as initialTouched, initialStatus, initialErrors. This is useful for altering the initial state (i.e. "base") of the form after changes have been made.
You can check this in more detail here.
And here is an example of resetting a specific field using resetForm() whereby you can see as you input name, email and upon submit only email field will get empty using resetForm.
import "./styles.css";
import React from "react";
import { Formik } from "formik";
const initialState = {
name: "",
email: ""
};
const App = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={initialState}
onSubmit={(values, actions) => {
console.log(values, "values");
actions.resetForm({
values: {
email: initialState.email
}
});
}}
>
{(props) => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
<br />
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
<br />
<br />
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
</div>
);
export default App;
I have a parent react function component (Products) which shows a list of products and with a state productInfo which is passed as a prop to the child component (AddEditProductModal)
const Products = () => {
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
const [productInfo, setProductInfo] = useState({});
const showEditModal = async (currentProductInfo) => {
console.log('edit called for key ',currentProductInfo.key)
setIsEditModalVisible(true);
setProductInfo(prevProductInfo => {
return {...prevProductInfo, ...currentProductInfo};
});
};
useEffect(() => {
setIsEditModalVisible(false);
setProductInfo({})
}, []);
return (
<>
<AddEditProductModal
title="Edit Product"
visible={isEditModalVisible}
productInfo={productInfo}
onOk={handleOk}
onCancel={handleCancel}
onFinish={onFinish}
/>
//Table components with columns/actions per row go here
</>
);
};
export default Products;
The child component AddEditProductModal is an antd Modal/Popup which fills the form with prefilled values chosen for current product row as shown below.
const AddEditProductModal = ({ title, visible, productInfo, onOk, onCancel, onFinish }) => {
return (
<Modal
title={title}
visible={visible}
onOk={onOk}
onCancel={onCancel}
>
<Form
name="basic"
labelCol={{
span: 8,
}}
wrapperCol={{
span: 16,
}}
onFinish={onFinish}
initialValues = {productInfo}
>
<Form.Item
label="Key"
name="key"
>
<Input disabled={true} />
</Form.Item>
<Form.Item
label="Image"
name="image"
rules={[
{
required: true,
message: "Please input image!",
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Name"
name="name"
rules={[
{
required: true,
message: "Please input name!",
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Price"
name="price"
rules={[
{
required: true,
message: "Please input price!",
},
]}
>
<Input />
</Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form>
</Modal>
);
};
export default AddEditProductModal;
The productInfo is an object containing props as shown below:
{"key":19,"name":"Cooker","image":"https://.....d16bfa6d9c2e010cadc3fe6885448cbd.jpg_720x720q80.jpg","price":123}
When I click on any row's Edit button, the AddEditProductModal shows correct default values for the product. But when I click on another product/row, the AddEditProductModal still shows old values even though the productInfo state (seen in the profiler) has changed. Basically, the productInfo state has changed in the parent but the child has not re-rendered is what I am thinking.
Can anyone help why the modal shows the info on the first click but second time, fails to re-render and shows old product info ?
Yes, you are right! First, antd Form's API initialValues only works when it is initialized or reset. Second, antd Modal won't destroy after the close. So there is the result you said.
method A: do what you said,
useEffect(() => {
form.setFieldsValue(productInfo);
}, [productInfo]);
method B: destroyOnClose property of Modal set true will also solve your issue, but this is not a good choice!
const AddEditProductModal = ({ title, visible, productInfo, onOk, onCancel, onFinish }) => {
return (
<Modal
title={title}
visible={visible}
onOk={onOk}
onCancel={onCancel}
+destroyOnClose+
>
...
I got the answer to my problem. Rather than react, this seemed like an issue with the antd Form component initialValues. Seems like the initialValues does not change and we need to explicitly update the values. Adding the following to my AddEditProductModal solved my issue:
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue(productInfo);
}, [productInfo]);
Hope this helps some antd user some day.
I am using React Hook Form. I've made a custom checkbox which looks like this:
const Checkbox = ({ text, className, setCheckbox, checkbox }) => {
const { register } = useFormContext();
const statute = register("statute");
return (
<Wrapper className={className}>
<StyledLabel>
<div>{text}</div>
<StyledInput
type="checkbox"
name="statute"
onChange={(e) => {
statute.onChange(e);
setCheckbox && setCheckbox(!checkbox);
}}
/>
<Checkmark />
</StyledLabel>
</Wrapper>
);
};
Checkbox (Checkbox.js) is nested in a parent component (Register.js):
const Register = ({ setRegisterView }) => {
const validationSchema = Yup.object().shape({
statute: Yup.bool().oneOf([true], "You need to accept the page statute."),
});
const methods = useForm({
mode: "onSubmit",
resolver: yupResolver(validationSchema),
});
const {
register: validate,
formState: { errors },
handleSubmit,
} = methods;
return (
<Wrapper>
<FormProvider {...methods}>
<Form onSubmit={handleSubmit(registerProcess)}>
<StyledCheckbox
text="Accept the page statute."
setCheckbox={null}
checkbox={null}
/>
{errors.statute && <Error>{errors.statute.message}</Error>}
<LoginButton type="submit">SIGN UP</LoginButton>
</Form>
</FormProvider>
</Wrapper>
);
};
export default Register;
The problem is that when I check the checkbox I get an information in errors.statute.message: statute must be a `boolean` type, but the final value was: `"on"`..
When I change this:
onChange={(e) => {
statute.onChange(e);
setCheckbox && setCheckbox(!checkbox);
}}
to this:
{...register("statute")}
then it works great and errors.statute.message shows correct message just when checked=false in checkbox input. But I need to have the extended version of onChange.
The problem is, that you not link the returned ref of the register call to your <StyledInput />. So just spread the return value of register - in the code example below i also omitted the name prop as it is included in statute. Here you can find all the props register will return.
<StyledInput
type="checkbox"
{...statute}
onChange={e => {
statute.onChange(e);
setCheckbox && setCheckbox(!checkbox);
}}
/>
What's the best way to store values typed into the text fields here?
const AddUserPage = () => (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<div className='main-content'>
<form className="ROOT" noValidate autoComplete="off">
<TextField id="standard-basic" label="Standard" />
</form>
</div>
</div>
);
export default AddUserPage;
I want to find a way such that I can use the stored values in my GraphQL mutations as well, without having to modify the const() structure of my page. I don't want to use the Class Component Extend or function structure here.
What is your const() structuremakes:
=> (This is the auto return syntax.)
If you want to store/reuse your value, you will have to define some state/variable to store the data.
You can also do it in upper component like:
import React, { useState } from "react";
const Parent = props => {
const [state, setState] = useState({ text: "" });
return <AddUserPage value={state.text} onChange={e => setState(prev => ({ ...prev, text: e.target.value || "" }))} />
}
const AddUserPage = ({ value = "" , onChange }) => (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<div className='main-content'>
<form className="ROOT" noValidate autoComplete="off">
<TextField id="standard-basic" value={value} onChange={onChange} label="Standard" />
// value, and Onchange comes from an upper component
</form>
</div>
</div>
);
I have a project and I use Formik and react-codemirror2, I want control the onChange in Formik but the onChange in codemirror2 don't have event...and i dont't know how to use...
let me explain better :
i have a formink:
<Formik
enableReinitialize
onSubmit={values =>
{this.submitFormDefinition(values)}}
initialValues={this.state}
>
{({ handleSubmit }) => (
<div>
<Form
onSubmit={handleSubmit}
autoComplete="off"
onChange={event => {
this.handleChange(event)
}}
>
<Field
component={CustomInputComponent}
name="name"
id="id"
multiline
/>
</Form>
</div>
)}
where I have my function handleChange:
handleChange = event => {console.log('change') ....}
an where the CustomInputComponent is my codemirror2 component:
const CustomInputComponent = ({ field, form, ...props }) => {
return (
<CodeMirror
id={props.id}
name={props.name}
value={this.state.value}
options={{
mode: 'javascript',
theme: 'default',
lineNumbers: true,
}}
{...props}
onBeforeChange={(editor, data, value) => {
console.log({ value })
this.setState({ jsonSchema: value })
}}
onChange={(editor, data, value) => {
?????????????
}}
/>
)
}
If I use another component as textField from Materia-UI it work i don't need to call on my CustomInputComponent the onChange , that is direct call by formink... because also the onChange of textField have as parameter event... but as you can see in the code the onChange of codeMirror doesn't have event...
I need to call the my handleChange and not onChange of codeMirror ...
I tried to do something like that:
<CodeMirror
onChange=form.handleChange
or use :
<CodeMirror
onKeyUp={form.handleChange}
or:
<CodeMirror
onKeyUp={(editor, event) => {
form.handleChange(event)
}}
but nothing works
my function handleChange is never call
How use react-codeMirror2 with Formik? is possible? how can I itercept the onChange of Formink?????
Formik's handleChange method expects an HTMLInput change event, and it gets the updated value from the input. Unfortunately, you don't get that convenience if you're using CodeMirror. However, CodeMirror provides the value of the component onBeforeChange, and Formik provides a setFieldValue method, which lets you imperatively set a value into state. So you can do this:
<Formik {...whateverPropsYouSet}>
(formikProps) => (
<CodeMirror
value={formikProps.values.yourDataField}
onBeforeChange={(_editor, _data, value) => {
formikProps.setFieldValue('yourDataField', value);
}
/>
)
</Formik>