Dynamic formik form and setFieldValue - reactjs

So I'm working on a client website that has a page with a date and time picker component. It also has a dynamic formik form that is created by som json coming from the CMS where they can build forms.
The form has 2 hidden fields. I change the form json when you select a date or time to save the values in the hidden for fields. But I ran into a problem getting formik to register the values when I submit. Showing the values in the DOM or even doing a console log shows the value. But I need to call setFieldValue before formik knows the values.
return (
<div className="o-container-small c-rte rich-text-wrapper">
<h2 className="display-3 mb-4">{title}</h2>
<p>{description}</p>
{submitSuccessful && successMessage ? (
<div className="mt-20px" dangerouslySetInnerHTML={{ __html: successMessage }} />
) : (
<Form onSubmit={handleSubmit}>
{({ setFieldValue, setFieldTouched }) => (
<div className="flex flex-wrap -mx-10px mt-30px">
{fields.map((f, index) => (
<div
className={
capitalizeFirstLetter(f.type) !== FormFieldType.Hidden ||
capitalizeFirstLetter(f.type) !== FormFieldType.HiddenDate ||
capitalizeFirstLetter(f.type) !== FormFieldType.HiddenTimeSpan
? `w-full ${!!f.size && `form-block--width-${f.size}/3`} px-10px pb-20px`
: ""
}
key={index}
>
<DynamicField
{...f}
submitPending={submitPending}
error={showError}
onChange={(v: string | number) => setFieldValue(f.id as string, v)}
onBlur={() => setFieldTouched(f.id as string)}
/>
</div>
))}
</div>
)}
</Form>
)}
</div>
);
The hidden field component looks like tis. The only way I can get it to work is if I do a useEffect to call onChange. But if I add onChange to my dependency array in the useEffect I get an endless loop.
import React, { FC, useEffect } from "react";
import { FormField } from "#cryos/shared/form";
import { IFormFieldProps } from "../field";
export type IHiddenFieldProps = IFormFieldProps;
export const HiddenField: FC<IHiddenFieldProps> = (props) => {
const { id, value, onChange } = props;
useEffect(() => {
if (value) {
onChange(value);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);
return (
<FormField name={id ?? ""} defaultValue={value}>
{({ field }) => {
field.value = value;
return <input type="hidden" {...field} value={field.value} />;
}}
</FormField>
);
};

Related

Is it possible to simple-react-code-editor as a Formik field component?

Trying to get this form field component to take a simple-react-code-editor:
not sure if I'm going about this the right way by trying to pass props form the useField hook, but it works for textfield tags, so thought the same method could apply to this as well. Although, I get the feeling the onValueChange callback is different from the onChange callback that this component doesn't have. Is there a way to add it somehow?
Editor Component:
const MyEditor = ({value, onChange}) => {
const highlight = (value) => {
return(
<Highlight {...defaultProps} theme={theme} code={value} language="sql">
{({ tokens, getLineProps, getTokenProps }) => (
<>
{tokens.map((line, i) => (
<div {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span {...getTokenProps({ token, key })} />
))}
</div>
))}
</>
)}
</Highlight>
)
};
return (
<Editor
value={value}
onValueChange={onChange}
highlight={highlight}
padding={'40px'}
style={styles.root}
/>
);
}
export default MyEditor;
Form with Field Component as MyEditor (tried using useField hook):
const FormQueryTextBox = ({...props}) => {
const [field] = useField(props);
return (
<MyEditor onChange={field.onChange} value={field.value}/>
)
}
const validationSchema = yup.object({
query_name: yup
.string()
.required()
.max(50)
});
const AddQueryForm = () => {
return (
<div>
<Formik
validateOnChange={true}
initialValues={{
query:""
}}
validationSchema={validationSchema}
onSubmit={(data, { setSubmitting }) => {
console.log(data);
}}
>
{() => (
<Form>
<div>
<Field
placeholder="query name"
name="query_name"
type="input"
as={TextField}
/>
</div>
<div>
<Field
name="query"
type="input"
as={FormQueryTextBox}
/>
</div>
</Form>
)}
</Formik>
</div>
)
}
components render without errors, but as I type the text doesn't appear.
I figured out that I just need to customize my onChange with the setter from the useFormikContext hook like this:
const FormQueryTextBox = ({...props}) => {
const [field] = useField(props);
const { setFieldValue } = useFormikContext();
return (
<MyEditor {...field} {...props} onChange={val => {
setFieldValue(field.name, val)
}}/>
)
}

Set defaultValues to Controllers in useFieldArray

Misunderstanding react-hook-forms.
I have a form for editing some stuff. Form contains fieldArray.
I set initial formData in useForm hook using default values
const methods = useForm({ defaultValues: defaultValues });
where defaultValues is
const defaultValues = {
test: [
{
name: "useFieldArray1"
},
{
name: "useFieldArray2"
}
]
};
And fieldArray. Here I'm using Controller (it's simplified case - in fact Custom input Controller more complex)
<ul>
{fields.map((item, index) => {
return (
<li key={item.id}>
<Controller
name={`test[${index}].name`}
control={control}
render={({value, onChange}) =>
<input onChange={onChange} defaultValue={value} />}
/>
<button type="button" onClick={() => remove(index)}>
Delete
</button>
</li>
);
})}
</ul>
When form is rendered everything is fine. Default values are displayed in input fields. But when I delete all fields and click append - new fields are not empty ... Default values are displayed
again. And it happens only with Controller. Why it happens ? And how I can avoid it?
Please, here is CodeSandBox link. Delete inputs and press append to reproduce what I am saying.
https://codesandbox.io/s/react-hook-form-usefieldarray-nested-arrays-forked-7mzyw?file=/src/fieldArray.js
Thanks
<Controller
name={name}
rules={rules}
defaultValue={defaultValue ? defaultValue : ''}
render={({ field, fieldState }) => {
return (
<TextField
inputRef={field.ref}
{...props}
{...field}
label={label}
value={field.value ? field.value : ''}
onChange={(event) => {
field.onChange(event.target.value);
props.onChange && props.onChange(event);
}}
style={props.style || { width: '100%' }}
helperText={fieldState?.error && fieldState?.error?.message}
error={Boolean(fieldState?.error)}
size={props.size || 'small'}
variant={props.variant || 'outlined'}
fullWidth={props.fullWidth || true}
/>
);
}}
/>

How do I implement a custom handleChange function on Formik?

In an input element, handleChange function would receive the event object from the onChange event. How do I create a custom handleChange function for non-input fields like the following?
import React from 'react';
import { useFormik } from "formik";
const SomeForm = () =>
{
const { handleChange, handleSubmit, values } = useFormik({
initialValues: {
type: `company`, name: ``,
},
onSubmit: values => {
console.log(JSON.stringify(values, null, 2));
},
});
return (
<div>
<form onSubmit={ handleSubmit }>
<label>Type</label>
<ul>
<li className={ values.type === `company` && `active` }
onClick={() => handleChange(/* some custom handle change */)} >
Company
</li>
<li className={ values.type === `individual` && `active` }
onClick={() => handleChange(/* some custom handle change */)} >
Individual
</li>
</ul>
<label>Full Name</label>
<input type="text"
name="name"
value={ value.name }
onChange={ handleChange } />
</form>
</div>
)
};
export default SomeForm;
use setField('fieldName',value) method of form object provided in render props pattern of Field component.
I think this is what you're after. You can add your custom code after field.onChange(e).
// Custom field
const MyTextField = ({ label, ...props }) => {
const [field, meta] = useField(props);
return (
<>
<input {...field} {...props}
onChange={e => {
// The original handler
field.onChange(e)
// Your custom code
console.log('I can do something else here.')
}}
className={ meta.error && 'is-invalid'}` } />
{meta.touched && meta.error && (
<div>{meta.error}</div>
)}
</>
);
};
And use it like so
<MyTextField name="entry" type="text" />

How to set state for text box in functional component

I am working on React JS. I have one text-box component and I want to show some default value in it. After that, the user should be allowed to change the value. Now I am unable to change the value. The text box is behaving like read-only. Below is my code
const EditStyleFormComponent = ({
submitting,
invalid,
}) => (
<form className={className} onSubmit={handleSubmit}>
<h2>LSPL (Low Stock Presentation Level)</h2>
<Line />
<InputGroup>
<TextFieldWithValidation name="lsplMan" label="LSPL Manual" input={{ onChnage:'', value: 'Current' }} />
</InputGroup>
</form>
);
Below is my TextFieldWithValidation code.
export const TextFieldWithValidationComponent = ({
meta,
input,
noStyles,
...otherProps
}) => (
<TextField
state={noStyles ? textFieldStates.DEFAULT : getState(meta)}
errorMessage={meta.touched ? meta.error : null}
{...input}
{...otherProps}
/>
);
Below is my TextField code.
const TextField = ({
className,
label,
description,
state,
errorMessage,
isEditable,
spaceAtBottom, // Not used, but we don't want it in otherProps
...otherProps
}) => {
const inputId = _.uniqueId();
return (
<div className={className}>
{label &&
<label htmlFor={inputId}>{label}</label>
}
<div className="input-group" id={isEditable ? 'editable' : 'readonly'}>
<input
id={inputId}
readOnly={!isEditable}
{...otherProps}
/>
{getStatusIcon(state)}
{errorMessage &&
<Error>{errorMessage}</Error>
}
{description &&
<Description>{description}</Description>
}
</div>
</div>
);
};
Can someone help me to fix this issue? Any help would be appreciated. Thanks
You can use State Hook for manage state in functional component.
Example :
const Message = () => {
const [message, setMessage] = useState( '' );
return (
<div>
<input
type="text"
value={message}
placeholder="Enter a message"
onChange={e => setMessage(e.target.value)}
/>
<p>
<strong>{message}</strong>
</p>
</div>
);
};
Yu defined onChange as empty string in EditStyleFormComponent component. So on any change input component just do nothing.
onChange should be some function that will update value.
If you want to use functional components there are two possible solutions:
Lift state up to parent component of EditStyleFormComponent (in case parent is class based component)
Use React Hooks like so (just example!)
const EditStyleFormComponent = ({
submitting,
invalid,
}) => {
const [inputValue, setInputValue] = useState ('Current'); // default value goes here
return <form className={className} onSubmit={handleSubmit}>
<h2>LSPL (Low Stock Presentation Level)</h2>
<Line />
<InputGroup>
<TextFieldWithValidation name="lsplMan" label="LSPL Manual" input={{ onChnage: (e) => { setInputValue(e.target.value); }, value: inputValue }} />
</InputGroup>
</form>
};

Creating a Dynamic on Demand Input field

I am new to React and doing a personal project to help with some organization in my life.
I have a field where I need to be able to add multiple names some times. I think I am close...I can't get the fields to appear but they act like they are all the same field, like they are bound together
Here is what I am getting
NameInput.jsx (component)
import React, { useState } from "react";
import { Form, Label } from "semantic-ui-react";
const NameInput = ({
input,
width,
type,
placeholder,
meta: { touched, error }
}) => {
let [inputs, setInputs] = useState([""]);
return (
<div className="nameField">
<Form.Field error={touched && !!error} width={width}>
{inputs.map((value, i) => (
<div>
<label>Name {i + 1}</label>
<input {...input} placeholder={placeholder} type={type} />{" "}
{touched && error && (
<Label basic color="red">
{error}
</Label>
)}
{e =>
setInputs(
inputs.map((value, j) => {
if (i === j) value = e.target.value;
return value;
})
)
}
</div>
))}
</Form.Field>
<button
className="ui compact button"
onClick={() => setInputs(inputs.concat(""))}
>
Add Additional Seller
</button>
</div>
);
};
export default NameInput;
And this is how I call the component. This stores to my firebase as nameField
<label>Name Field</label>
<Field
name="nameField"
type="text"
component={NameInput}
placeholder="Enter Full Name"
/>
Ideally, I'd want it to save has nameField, namefield2, nameField3 but I believe I can get that part solved on my own if I could just get my component to play nice.
Haven't you ever get a warning that a key should be provided for list items?
You should assign a unique key for each input div.

Resources