I have a Ant Design 4.x.x Form element without multiple Form.Item. I need to implement some logic involving form items' values, for example disabling a field if another one's value equals something, or recalculate select options when a text input changes.
To do so, I create the form using Form.useForm() and use form.getFieldValue() in my functional component body and / or in the returned JSX, like so :
It is working as I expect to, but at startup, getFieldValue usages throw annoying
index.js:1 Warning: Instance created by `useForm` is not connect to any Form element. Forget to pass `form` prop?
I found that Form functions cannot be used before rendering, and the problem also occured when displaying a form in a Modal like stated in the docs.
So I feel that I'm missing something on how to correctly add custom logic between fields, or doing some calculation involving fields values in component body.
What would be a correct approach to do this ?
Try adding getContainer={false}, to your modal this will work for you.
Related
I have a piece of code where Formik is used in the #React app with #Typescript and Yup validation. The problem I am facing is that on setting the values in a Select element it does not change the value at all or perhaps it immediately resets the value to the initial value as given in the Formik initial value object. What should I do about it? (No code is available here. But hopefully)
This issue is resolved.
In my case enableReinitialize is the culprit.
It was just the enableReinitialize property in the initial Values object set to true. You just need to remove this and you are good to go. I tried dozens of patches to cope with the problem and due to this property, each and every property was not working according to the logic. I also used #MUI #DatePicker #MUIDatePicker which was having issues because of this one property.
Hope it helps many others.
#Coding
I am implementing a form in react using react hook form, The form has two select fields country and states.
Second field changes the option based on the selection in first field.
Please see the below sandbox for more details
Creating/submitting the record works perfectly fine.
The problem is: In edit, when I pre populate the values in the form using setValue(), it does not set the second dropdown(state select in the sandbox below) values on the UI but it shows that it has set the value to the field(see in the console for field state).
[CodeSandBox] https://codesandbox.io/s/angry-murdock-h0lbsp?file=/src/App.js
Steps to reproduce:
Open this sandbox in the browser.
Click on the SET ALL VALUES button.
See the blank value in states select
Also, Whats the best way to populate a form like this, i.e. in defaultsValues or useEffect?
What am I missing here, so putting it for the experts here.
Thanks for your time.
The problem you are having is about setValue. This function does not trigger a re-render in most cases. You can use reset instead.
https://react-hook-form.com/api/useform/reset
Also, if you'd like to fill the form without any user interaction, use reset in useEffect with proper dependencies.
Lastly, If you'd like to have just them having initial values instead of being undefined, set defaultValues. It is also recommended in official documents:
Important: You should provide a proper default value and avoid undefined.
undefined is reserved for fallback from inline
defaultValue/defaultChecked to hook level defaultValues. undefined value is conflicting with controlled component as default state
https://react-hook-form.com/api/useform
I'm getting my inputs dynamically from an API call based on a change in select input, but when I try to add to the initial values of Formik, it always gives me an error ...
Warning: A component is changing an uncontrolled input of type text to be controlled.
And it doesn't help if I set enableReinitialize={true} to Formik.
However, if I generated the inputs from a local JSON or object, the error goes away.
What am I doing wrong here ...
https://codesandbox.io/s/test-dynamic-inputs-with-formik-xr9qg
The form submits fine though.
Better use enableReinitialize={true}. This is official Formik API.
You can check this issue
If anyone is facing the same issue, I just found the solution ...
You have to set value={field.value || ''} in the input inside the TextInput component or whatever type you're using in order to fix this issue.
I had a complex, dynamic form and also came across this issue. There are a few things that I'd recommend for anyone debugging this issue in the future:
Set value for your Field component--like Ruby does above.
Ensure that your Formik component has a uniquely identifying key.
Track and debug your initialValues and ensure that all fields are accounted for. You shouldn't have to set the field value like Ruby does--so long as your initialValues object accounts for all of the fields. However, my form dynamically changed Field components--and Ruby's solution is the only one that worked.
If your form is not dynamic--I think it might be best to check your initialValues object first before implementing Ruby's solution. Formik should be taking care of those values for you--which is why it's such an awesome tool.
i've checked with enableReinitialize={true}. But its not working as much as expected. so wrote a useEffect like
useEffect(() => {
formik.setFieldValue('query_string', active?.query);
}, [active])
it's worked !
I have an AngularJS custom component that essentially wraps around a Material Design mdSelect, but provides easy configuration of available, default, and current values via its bindings.
But the component functions as a general editing component in an mdDialog that can change its options based upon the thing being edited. Thus is a "Next" button to go to the next "thing" to be edited. When the button is pressed the custom component will have new available, default, and current values—something like this:
<foo-component default-value="dialog.getDefaultFoo()" current-foo="dialog.currentFoo">
</foo-component>
Note that the component, if a list of available values is not given (as in the example above), the component assumes a list of values with only one value, the "default-value" indicated.
So when a user selects "Next", the list of values in the mdSelect will change, because the value returned by dialog.getDefaultFoo(). The new selected value will be dialog.currentFoo.
But if dialog.currentFoo is null, I want the control to automatically select the indicated default value, or if no default is indicated, the first available value. That's easy enough when the component is created using $onInit. But once it is created, how do I know (inside the component) that the user has selected "Next" and the list of available values has changed?
In the code for the "Next" button, I call this.fooForm.$setPristine(). According to the documentation, the when this method is called the form controller will "propagate to all the controls contained in this form." So I considered having my custom control hook detect that $setPristine() is being called, so that it can automatically select a default value from the list if the new value is null. But how now I'm back in the same situation: how does my custom component detect that $setPristine() is being called on the form?
In essence, my custom component needs to detect when one of its bound values changes, and perform some custom update of other values under certain conditions. I know that I can use a getter/settter from outside the custom component, but how does the custom component detect that one of its bound values has changed?
To make matters more complicated, dialog.currentFoo is actually a function, which my component recognizes as a getter/setter function which will return/update the correct value based upon the state of the dialog. So I can't even detect that this value has changed, because the actual function never changes—only the value that it returns will change.
And it's actually even more complicated than that, because the mdSelect is only one piece of the object that gets sent to dialog.currentFoo; otherwise it isn't propagated outside the component.
Trying to summarize, I need to know in a custom component if the binding dialog.currentFoo, which is really a getter/setter method, would now return null so that the custom component could select a default value (also dynamic) based upon the current items (also dynamic) listed in the internal mdSelect. I would accept workarounds, such as detecting that $setPristine() has been called on the enclosing form. I would even accept a hack, such as forcing AngularJS to recreate the custom component when some external state changes.
This is tricky, because AngularJS is tricky, and because it's exceedingly hard to track information on custom AngularJS control/input components.
Above all the first thing that needs to be done is to make the custom component use the normal ngModel framework; trying to "fake" it using a two-way bound currentValue doesn't cut it for this complexity, and besides, ngModel is the "correct" AngularJS way to do it. To pull this off you'll need to use NgModelController. Finding out how to do this with a component is difficult in itself, although one page gave the magic formula. It goes something like this:
require: {
ngModelCtrl: "ngModel"
},
bindings: {
ngModel: "<"
//TODO add defaultValue or whatever other properties here
},
Then you don't access the two-way value directly; you use the NgModelController interface:
//get the current value
const foo = this.ngModelCtrl.$modelValue;
//set the current value
this.ngModelCtrl.$setViewValue(foo);
As best I can tell, if you add ng-model-options="{getterSetter:true}" to the component, the ngModelOptions will automatically get applied to your model, and getters/setters will be called automatically.
Now you are hooked into the whole ngModel framework, and use NgModelController to add/access validators and all sorts of advanced features.
Getting back to my needs, where I need to "reset the component" in certain conditions, I can patch into NgModelController#$setPristine(). The form controller recognizes my component is part of the ngModel framework, and calls $setPristine() when the form is reset to its pristine state.
this.$onInit = () => {
this.ngModelCtrl.$setPristine = () => {
myInternalValue1 = null;
myInternalValue2 = this.defaultValue;
};
this.ngModelCtrl.$setPristine(); //initialize default value
};
This explains the doubt I had about the form controller "propagat[ing $setPristine()] to all the controls contained in this form", which I mentioned in my original question. The secret is that the component must become part of the real ngModel system so that it can interact as AngularJS is expecting, as well as to gain access to important internal methods.
I have a specific need to only make one input field in a form (legacy code) a react component. So, I wrap the input with a div and render into it the 'new' input field that needs some special behavior.
The problem arises because the input field is no longer editable. I try to type into it.. nothing. I narrowed it down to the following:
<input type="text" **data-reactid=".2.0.0.0.1.0.0.1.2.0"**
When I remove that "data-reactid....", by editing via console, it works.
So when I am using react to sub out one form input field with a react one, it doesn't work unless I manually remove that data-reactid..
Is there a workaround for this, or a reason why this is happening?
Well its just a data attribute written by react to help them render into the DOM more efficiently so it should have no real impact on a input element or any element (unless there is code or style explicitly disabling the input) - I realize that this is no real help - because it happens to you, but this is not typical of react apps with inputs or element with data-attributes.
But if its the only bit of react on the page then that id is a bit long and I would have expected something like ".0" or ".0.0" if its wrapped in a div that react controls.
The react-id is only used by the React engine to work out what elements of the DOM need to be re-written when there are changes to state or props in your components.
One thing I noticed is, typically there would be an ID or in react a ref that you applied to the input in order to interact with it (such as getting its value).
I include the mark-up from a simple entry box on the user login form of a working app, as you can see it's not significantly different from what you have and works on all browsers Windows and Mac down to IE8 included.. (but not any IE below 8) and you need various shims for getting it work on IE8.
<input class="username-text-field" id="user-id" type="text" data-reactid=".0.0.0.1.3.0.0.2">
If none of these apply or you have them covered then practicably here should be no reason why your input should be disabled. It should just act like any other input. Have tried just dropping you component onto a simple HTML page with only the input on it, just to debug through the component in isolation?
That said,
It does feel that loading the entire React engine and wiring up a component to allow a single input field is a little over-kill. I realize that you're trying not to have to recreate exactly the same functionality you already have in react again on the legacy form, but if your render function is not too onerous then maybe a simple bit of JavaScript or JQuery might be the answer as a one off in the legacy solution (rather than the hit for the library) - just a thought