Is it possible use react-codemirror2 with formik? - javascript

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>

Related

MUI + React Hook Form: Fill out TextField value but then can't modify the value

I'm using the combination of MUI + React Hook Form, so I've created a CustomTextField.tsx component to make it worked.
// CustomTextField.tsx
import { TextField } from "#mui/material";
export default function CustomTextField(props: any) {
return (
<Controller
name={props.name}
control={props.control}
render={({
field: { onChange, value },
fieldState: { error },
formState
}) => <TextField onChange={onChange} {...props} />}
/>
);
}
Then at the app/parent level, I want to these steps:
Fetch data and display to the TextField.
Modify the text in TextField
Submit the new value in TextField
This is my approach:
//App.tsx
export default function App() {
const { control, handleSubmit } = useForm();
const [fetchedData, setFetchedData] = useState<string>("");
...
return (
...
<CustomTextField
control={control}
name="description"
label="Description"
type="text"
variant="outlined"
value={fetchedData ? fetchedData: ""} //<-- fetched data is set to value of TextField to display
/>
...
);
}
With this approach, I managed to display the fetchedData on TextField UI, but can not modify that data on text field. Also when I submit, the data is not correct to what display on the TextField
I have created a codesandbox link for this: https://codesandbox.io/s/blissful-sanne-x858dx?file=/src/App.tsx:190-1155
How can I display the fetchedData, but also can modify the data in text field and then submit through React Hook Form later?
What you are trying to is have a text input where the user can type a value, but you can also set the value externally by clicking the "Fetch Data" button.
Your setup includes conflicting sources of truth. The value of the field is set to the data state and the value which is stored in the react-hook-form state is ignored.
What you want to do is to modify the internal state of the react-hook-form when the button is pressed.
You can delete the local state:
const [data, setData] = useState<string>("");
Instead, you can use the setValue helper function from the form:
const { control, handleSubmit, setValue } = useForm<FormValues>();
const fetchingData = () => {
setValue("description", "fetched description Text");
};
In your CustomTextField, make sure that you set the value of the input to the value from the form state.
render={({
field: { onChange, value },
fieldState: { error },
formState
}) => <CusTextField onChange={onChange} value={value} {...props} />}
Complete Code
Inspired by #Linda solution, I've came up with this approach, that ensures the functionality working with the style of MUI TextField as well.
The setValue will be passed down to custom TextField to set the value. And to keep the {...props} functionality, I delete the setValue props before spreading it on MUI TextField.
//CustomTextField.tsx
import { TextField } from "#mui/material"
import { Controller } from "react-hook-form"
export default function CustomTextField(props: any) {
const propsObj = { ...props }
delete propsObj.setValue
return (
<Controller
name={props.name}
control={props.control}
defaultValue={""}
render={({
field: { onChange, value },
fieldState: { error },
formState,
}) => (
<TextField
value={value}
onChange={({ target: { value } }) => {
onChange(value)
if (props?.setValue) props.setValue(props.name, value)
}}
{...propsObj}
/>
)}
/>
)
}
//App.tsx
export default function App(){
const { control, handleSubmit, setValue } = useForm()
return (
<form onSubmit={handleSubmit(...)}>
<CustomTextField
control={control}
name="description"
label="Description"
type="text"
variant="outlined"
setValue={setValue}
/>
<form/>
)
}

How to set Formik custom component value to Formik value

I'm using Formik for my form with google place auto-complete, I want to render places auto-complete as a custom component in the Formik field.
form.js
<Formik initialValues={location:""}>
<Field name="location" component={PlacesAutoComplete} placeholder="enter your location"/>
{...rest of form}
</Formik>
auto-complete component
import PlacesAutocomplete , {
geocodeByAddress,
geocodeByPlaceId
} from "react-google-places-autocomplete";
export const PlacesAutoComplete = ({
field: { name, ...field }, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
classes,
label,
...props
}: any) => {
const [fieldName, setFildName] = React.useState(field.name);
const [address, setAddress] = React.useState(props.value || "");
const error = errors[name];
// const touch = touched[name];
const handleSelect = () => {
// set this value to formik value
};
const handleChange = () => {
// set this value to formik value
};
const handleError = () => {
props.form.setFieldError(fieldName, error);
};
return (
<PlacesAutocomplete
value={address}
onChange={handleChange}
onSelect={handleSelect}
onError={handleError}
name={name}
placeholder={props.placeholder}
id={name}
{...props}
apiKey="Api key here"
>
{({
getInputProps,
suggestions,
getSuggestionItemProps,
loading
}: any) => (
<div>
<input
{...getInputProps({
placeholder: "Search Places ...",
className: "location-search-input form-control"
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map((suggestion: any) => {
const className = suggestion.active
? "suggestion-item--active"
: "suggestion-item";
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: "#fafafa", cursor: "pointer" }
: { backgroundColor: "#ffffff", cursor: "pointer" };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
);
};
How I set places auto-complete value to formik value, I'm pretty new to react and confused in handle change and on change functions. also, I found a solution in react class component here, But when converting those codes into functional components I'm stuck in Onchange and onSlecet functions
Better not write functional components as you'll get stuck with the test cases if you are writing.
OnChange is even you type anything, the value gets stored in onChange.
Abe onSelect is when you select anything
Basically on change you need to call formik's field onChange function. So in case you get an event on handleChange, just do this
const handleChange = (event) => {
// set this value to formik value
field.onChange(event.target.value)
};
or in case you get value in handleChange then do this
const handleChange = (value) => {
// set this value to formik value
field.onChange(value)
};
This will sync your formik state with autocomplete state.
Now comes the part for select. In this case also you can take the same route
const handleSelect = (value) => {
// set this value to formik value
field.onChange(value)
};
or you can use the setField function of form to update the value
const handleSelect = (value) => {
// set this value to formik value
form.setField('location',value)
};

Forkmik handleChange not updating dropdown value in React.js

I'm trying to use Form.Select from semantic ui react to build a dropdrown. I'm sending an array of objects with the following properties to fill dropdown options:
DelivererForDropdown = {
key: deliverer.id,
value: deliverer.id,
text: deliverer.userName,
id: deliverer.id
};
The problem I'm facing is that the handleChange event from Formik is not updating my selection. I'm using this event for other inputs and it works well.
<Form.Select
type="text"
placeholder="Repartidor"
name="deliverer"
options={deliverersForDropdown}
search
onChange={handleChange}
error={errors.deliverer}
value={values.deliverer}
/>
You can use Formik function setFieldValue() to manually set the value in the handler onChange():
<Formik
enableReinitialize={true}
initialValues={initialValues}
onSubmit={async (values, { resetForm }) => {
console.log(JSON.stringify(values, null, 2));
}}
>
{({ isSubmitting, values, setFieldValue, setTouched, errors }) => {
return (
<FormikForm>
<Form.Select
type="text"
placeholder="Repartidor"
name="deliverer"
options={deliverersForDropdown}
search={true}
onChange={(event, { value }) => {
setFieldValue("deliverer", value);
setTouched("deliverer", true);
}}
error={errors.deliverer}
value={values.deliverer}
/>
<button type="submit" disabled={isSubmitting}>
SUBMIT
</button>
</FormikForm>
);
}}
</Formik>
Live Demo
Alternatively you can use a 3rd party that automatically binds, for example formik-semantic-ui

React Hook Form validation using nested custom checkbox component

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);
}}
/>

#Antd# How can I bind self-made component with Form.item

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

Categories