SetValues Formik Function disables Formik HandleSubmit - reactjs

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()
}

Related

React-Phone-Number-Input + React-Hook-Form: How to get current country code in controller validation?

I'm using react-phone-number-input library. The phone number input is not required but if the number is present I wish it could be validated before the form is sent.
If the cellphone field is pristine / left untouched when form is submitted then isValid accepts undefined (valid state).
If country code is changed right away (without actually inputting the number) isValid accepts the selected country's calling code (e.g. +46 for Sweden). This is still a perfectly valid case.
When accessed in the isValid function the phoneCountryCode always holds the previous selected value. So there's always a disparity between the validated phone number and the current country code. I'm not sure if the problem is library-specific, maybe it's my mistake. How do I get rid of the mentioned disparity?
I made a reproduction on CodeSandbox.
import PhoneInput, {
parsePhoneNumber,
getCountryCallingCode
} from "react-phone-number-input";
const [phoneCountryCode, phoneCountryCodeSetter] = useState('DE');
<Controller
name="cellphone"
rules={{
validate: {
isValid: value => {
if(value) {
const callingCode = getCountryCallingCode(phoneCountryCode);
if(! new RegExp(`^\\+${callingCode}$`).test(value)) {
// The parsePhoneNumber utility returns null
// if provided with only the calling code
return !!parsePhoneNumber(value);
}
}
return true;
}
}
}}
control={control}
render={({ field }) => (
<PhoneInput
{...field}
onCountryChange={(v) => phoneCountryCodeSetter(v)}
limitMaxLength={true}
international={true}
defaultCountry="DE"
/>
)}
/>
It's a react specific, nothing wrongs in the library. React never update state immediately, state updates in react are asynchronous; when an update is requested there's no guarantee that the updates will be made immediately. Then updater functions enqueue changes to the component state, but react may delay the changes.
for example:
const [age, setAge] = useSate(21);
const handleClick = () => {
setAge(24)
console.log("Age:", age);
}
You'll see 21 logged in the console.
So this is how react works. In your case, as you change country this "onCountryChange" event triggers updating function to update the state and to validate the phone number but the state is not updated yet that's why it is picking the previous countryCode value(Do console log here).
To understand this more clearly put this code inside your component.
useEffect(() => {
console.log("useEffect: phoneCountryCode", phoneCountryCode);
}, [phoneCountryCode]);
console.log(phoneCountryCode, value); // inside isValid()
useEffect callback will be called when phoneCountryCode value is updated and console.log inside isValid() will be called before the state gets updated.
Hopes this makes sense. Happy Coding!!

Formik: prevent touching fields (do not mark as .touched) on Submit

I have a situation where I need to prevent Formik from marking as .touched my fields when I Submit. .touched should be set when I touch a field, but clicking Submit should reset .touched to {}. I read in the docs that
Before submitting a form, Formik touches all fields so that all errors
that may have been hidden will now be visible.
I have my own custom isInvalid= definition and my own submitClicked variable that I track myself, so I need to turn off or reverse this behavior. I want errors populated, but I want touched to be empty (reset) after clicking Submit.
I was thinking of calling setTouched({}) somewhere, but I need an event that tells me validation has completed. I also don't know where to insert it; I can't call it from a custom useEffect that watches submitClicked.
<Button type="submit"
onClick{() => {
// my own var.
setSubmitClicked(true);
// If I call setTouched({}) here, it does nothing. It gets overridden after Form Submit
setTouched({});
}} Submit
</Button>
also, can't really do it in a useEffect, I don't have access to Formik here and it's a mess to implement,
useEffect(() => {
someFormikContext.setFieldTouched({}); // hard to get Formik Context here
}, [submitClicked]);
Are there any simpler solutions?
You can reset form on submit ,
onSubmit={(values, { resetForm }) => {
// do your stuff
resetForm();
}}
This will reset your form errors / touched , hope this will help you

Show parameter of an element on React js dynamically

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.

change value of input made with react from chrome extension

I work on Chrome extension, i need to update lot of inputs of an html page made with React from numbers readed from CSV. I cannot update the web site.
-
An example of input copied from the rendered website :
<td><input class="input input_small fpInput" value="29,4"></td>
-
How it's made (not sure 100% about that, had to read the uglified js source)
{
key: "render",
value: function () {
return s.a.createElement("input", {
className: "input input_small fpInput",
value: this.state.value,
onChange: this.handleChange,
onBlur: this.handleSubmit,
onFocus: this.handleFocus
})
}
}
-
Each time you change the input value a function is called and a POST is made to save it.
I want to trigger the onBlur() or onChange() from my extension after i changed the input value to trigger the POST
I tried this :
var el = document. ... .querySelector('input'); // the selector is simplied of course
el.value = 321;
el.onChange(); // ERROR onChange is not a function
el.onchange(); // ERROR onchange is not a function
el.handleChange(); // ERROR handleChange is not a function
Any idea please ?
You can't call a React component's method directly from the DOM element it has rendered. You need to trigger an event that bubbles up so that React can catch it at the document level and process it normally, as it would do with a real one.
✨ Document.execCommand():
As pointed out by #woxxom in the comments, the easiest way to do that might be to focus the inputs and then use Document.execCommand():
const input1 = document.getElementById('input1');
const input2 = document.getElementById('input2');
input1.focus();
document.execCommand('insertText', false, 'input1');
input2.focus();
document.execCommand('insertText', false, 'input2');
<input id="input1" />
<input id="input2" />
⚒️ Manually Dispatching Events:
Otherwise, you might try manually dispatching a change, input and/or blur event using the Event() constructor in those fields after you change their value.
Also, note Event()'s second optional argument contains a field, bubbles, that is false by default. You need that one to be true. Otherwise, this won't work, as React is listening for events on the document.
Additionally, you might need to use Element.setAttribute() too, as that will update the value attribute on the DOM (the initial value on the field), while element.value will update the current value (and so the display value). Usually, though, it's not needed. For more on this see What is the difference between properties and attributes in HTML?.
This approach might have some timing issues you might need to handle using setTimeout when updating multiple inputs, so if the first one works, I'd go for that one instead.
const input1 = document.getElementById('input1');
// This updates the value displayed on the input, but not the DOM (you can do that with setAttribute,
// but it's not needed here):
input1.value = 'input1';
// Dispatch an "input" event. In some cases, "change" would also work:
input1.dispatchEvent(new Event('input', { bubbles: true }));
// It looks like only one field will be updated if both events are dispatched
// straight away, so you could use a setTimeout here:
setTimeout(() => {
const input2 = document.getElementById('input2');
input2.value = 'input2';
input2.dispatchEvent(new Event('input', { bubbles: true }));
});
<input id="input1" />
<input id="input2" />
To elaborate a bit more on #varoons answer, which is factually correct albeit a bit short on explanation.
You can do so by injecting (dispatching, in browser terms) the event into the dom:
// Needs setAttribute to work well with React and everything, just `.value` doesn't cut it
// Also changed it to a string, as all attributes are strings (even for <input type="number" />)
el.setAttribute("value", "321");
// As #wOxxOm pointed out, we need to pass `{ bubbles: true }` to the options,
// as React listens on the document element and not the individual input elements
el.dispatchEvent(new Event("change", { bubbles: true }));
el.dispatchEvent(new Event("blur", { bubbles: true }));
This will actually call all the listeners, even those made with React (as is the case in your de-uglyfied code ;)) or made with simple element.onChange = () => {...} listeners.
Example: https://codesandbox.io/s/kml7m2nn4r
el.dispatchEvent(new CustomEvent("change"))

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