Show parameter of an element on React js dynamically - reactjs

Using Formik, I have created a form that works fine, but to handle the submission I want to paint the onSubmit parameter on the form element if a variable of the state is true.
The state is the following:
this.state = {
formDataOk: false,
}
After validating all the inputs of the form, if everything is fine formDataOk is changed to true. When this happens I am trying to show the onSubmit parameter this way:
<Form {this.state.formDataOk && onSubmit={this.handleSubmit}}>
Where handleSubmit is a function.

Related

useForm in React: formState.isDirty is true, but .dirtyFields is empty

I have a React JS form using useForm library.
This is a popular library so I'm not sure this can be a bug but I'm getting into a state which seems incorrect. See the screenshot of the console in chrome below:
How can I have formState.isDirty, but no formState.dirtyFields?
It's a large, complex form but the isDirty state is achieved by human interaction with the form, with registration looking like this:
<input id="value" {...register("value")} defaultValue={variable.value} />
One other observation - the behavior seems to change when observed. For example, when adding this code to the form - it seems to work more as expected:
<div>
{formState.isDirty ? "DIRTY" : "CLEAN"}
<pre>dirty {JSON.stringify(formState.dirtyFields, null, 2)}</pre>
<pre>touched {JSON.stringify(formState.touchedFields, null, 2)}</pre>
</div>
It seems obvious now, but the problem was I had a dependency on formState in a function called on button click. Because clicking the button doesn't cause a re-render of the form, the fresh value of formState is not retrieved from useForm - meaning it is stale.
The trick is to have this change trigger a re-render. You can do this by having it in rendered in the output (so .tsx/.jsx spots the dependency) or by using useCallback to create the save function and register formState in the dependencies of that function, a la
const save = useCallback(() => {
// save using formState dirtyFields, touchedFields etc
}, [formState]);
// ...
<button onClick={save} />
Now, it works great and this explains the behavior change when showing the dirty state in the HTML output.

Conditionally enabling a form button with react-hook-form and material ui

I'm creating a React form with Material UI. My goal is to force the user to answer all the questions before they can click the download button. In other words I'm trying to leave the download button in the disabled state until I can determine that values are set on each field. I've tried to get this working with and without react-hook-form.
What I've tried
Without react-hook-form...
I have my example in coding sandbox here:
https://codesandbox.io/s/enable-download-button-xwois4?file=/src/App.js
In this attempt I abandoned react-hook-form and added some logic that executes onChange. It looks through each of the formValues and ensures none of them are empty or set to "no answer". Like this:
const handleInputChange = (e) => {
// do setFormValues stuff first
// check that every question has been answered and enable / disable the download button
let disableDownload = false;
Object.values(formValues).forEach((val) => {
if (
typeof val === "undefined" ||
val === null ||
val === "no answer" ||
val === ""
) {
disableDownload = true;
}
});
setBtnDisabled(disableDownload);
The problem with this approach, as you'll see from playing with the UI in codesandbox, is that it requires an extra toggle of the form field value in order to detect that every field has been set. After the extra "toggle" the button does indeed re-enable. Maybe I could change this to onBlur but overall I feel like this approach isn't the right way to do it.
Using react-hook-form
With this approach...the approach I prefer to get working but really struggled with, I ran into several problems.
First the setup:
I removed the logic for setBtnDisabled() in the handleInputChange function.
I tried following the example on the react-hook-form website for material ui but in that example they're explicitly defining the defaultValues in useForm where-as mine come from useEffect. I want my initial values to come from my questionsObject so I don't think I want to get rid of useEffect.
I couldn't figure out what to do with {...field} as in the linked material ui example. I tried dropping it on RadioGroup
<Controller
name="RadioGroup"
control={control}
rules={{ required: true }}
render={({ field }) => (
<RadioGroup
questiontype={question.type}
name={questionId}
value={formValues[questionId]}
onChange={handleInputChange}
row
{...field}
>
but I get an MUI error of : MUI: A component is changing the uncontrolled value state of RadioGroup to be controlled.
Also, I don't see that useForm's state is changing at all. For example, I was hoping the list of touchedfields would increase as I clicked radio buttons but it isn't. I read that I should pass formState into useEffect() like this:
useEffect(() => {
const outputObj = {};
Object.keys(questionsObject).map(
(question) => (outputObj[question] = questionsObject[question].value)
);
setFormValues(outputObj);
}, [formState]);
but that makes me question what I need to do with formValues. Wondering if formState is supposed to replace useState for this.

SetValues Formik Function disables Formik HandleSubmit

I have a form that can handle submission a couple of different ways. The difference is managed by a flag in the form Values that can either be true or false.
I am running into this really weird issue where when I use the Formik setValues() function the form doesn't enter the handleSubmit function at all. It just stops execution. However if I set the value using just by going this.props.values.x = ...
it enters the function and continues with submitting the form just fine.
Why is this happening?
there's really no point in showing code as the description tells you everything but this is what the submit handler looks like:
A confirmation function calls the submit handler which sets the value and then attempts to call handleSubmit
<Confirmation
items={this.confirmationData()}
isLoading={this.props.isSubmitting}
open={isConfirming}
preapproval={true}
submitAnother={this.submitAnother}
onClick={this.submit} //this is the submit handler
onClose={() => this.setState({ isConfirming: false })}
/>
submit = () => {
this.props.setValues({ ...this.props.values, submit: true})
this.props.handleSubmit()
}
if the first line is changed to this.props.values.submit = true, the form submits however, using the setValues function, The application stops executing after setting the submit value to be true.
Turns out the issue was the is Validating Flag being set to true when you call setFieldValue so the simplest solution is to pass a third argument to setFieldValue to manually turn off validation:
submit = () => {
this.props.setFieldValue('submit', true, false)
this.props.handleSubmit()
}

How to submit #atlaskit/form remotely

I want to submit a #atlaskit/form from the button outside the form. I have gone through https://atlaskit.atlassian.com/packages/core/form but no documentation regarding this
Warning: When trying to submit a form remotely, you would need to go out of your way to actually make the validation work. This is applicable to HTML forms, thus not limited by Atlaskit forms.
Read about it here:
How to force a html5 form validation without submitting it via jQuery
https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation
Answer:
Atlaskit form actually renders the native html form underneath. Therefore,
we can attach a ref to the Form element and then trigger submit of the form property of the current ref.
Example:
// attach the ref to form
class extends React.Component{
form = React.createRef();
render() {
<Form
ref={this.form}
{...props}
>
{children}
</Form>
}
}
Trigger submit on html form:
this.form.current.form.submit()
See example codesandox here.

Dynamically set a property value for an Input in Semantic UI React

I have an Input element that I want to display an error on when the form validation fails.
<Input ref="amount" error={false} />
When the user enters an incorrect amount, I want to change "error" to "true". How can this be done?
I have tried:
this.refs.amount.props.error = true;
Which seems bad but I'm not sure how else. If I add a conditional statement in the definition of the Input element, that seems to only evaluate once and then remain the same. Do I need to force an update on the element? If so, how?
Yes it's possible to validate the input when the form is submitted.
All you need is to keep track on input value and use same approach as #SajithDilshan for the input error.
this.state = {
error: false,
value: ''
}
...
render(){
return
...
<Input
ref="amount"
value={this.state.value}
error={this.state.error}
/>
...
}
Then onSubmit should looks like:
onSubmit(e){
const isError = this.state.value === '';
this.setState({error: isError});
// rest of your logic
}
Hope it will help!
Use the onChange() method on the input as below.
<Input ref="amount" onChange={this.onInputChange} error={this.state.error} />
After that implement the onInputChange() method as below in your component.
onInputChange = (e) => {
if (e.target.value === "") { // logic to validate the input
this.setState({error: true});
} else {
this.setState({error: false});
}
}
Note that this will add error property to the state.
Further, you should not modify the props within a component. Props are passes from parent component to the child component as immutable inputs.
This is not exactly the answer, but still:
This type of fiddling with each possible state of form element (valid, invalid, warning, show tooltip, was edited, in focus, left focus, was submitted, submit failed or not, etc) becomes to much trouble when the form grows beyond 1 input field.
I would suggest to use redux-form package that integrates with semantic-ui-react` almost perfectly and provided you have provided it with the validate function does everything else for you. It takes some time to understand the basics of it, but it really pays.

Resources