onGetErrorMessage not being called for FluentUIReact TextField - reactjs

I was going through fabric UI TextField documentation(https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield ) . I tried few examples of TextField with the following two handlers:-
onChange
onGetErrorMessage.
I tried few experiments with these two handlers (https://codepen.io/nishchay271095/pen/rNyPKYY?editors=1011 ):-
Case 1. When using both the handlers without any extra properties of TextField, both handlers(onChange and onGetErrorMessage) are called.
<TextField
label="Controlled TextField"
onChange={onChangeSecondTextFieldValue}
onGetErrorMessage = {onGetErrorMessageHandler}
/>
Case 2. When using both handlers with "defaultValue" property, both handlers(onChange and onGetErrorMessage) are called.
<TextField
label="Controlled TextField"
defaultValue = "hello"
onChange={onChangeSecondTextFieldValue}
onGetErrorMessage = {onGetErrorMessageHandler}
/>
Case 3. When using both handlers with "value" property of TextField, only onChange() handler was called.
<TextField
label="Controlled TextField"
value="hello"
onChange={onChangeSecondTextFieldValue}
onGetErrorMessage = {onGetErrorMessageHandler}
/>
Now, I'm having following doubts:-
Why onGetErrorMessage() is not being called in case 3?
How value and defaultValue properties affect the behavior of these two handlers(onChange and onGetErrorMessage)?

Why onGetErrorMessage() is not being called in case 3?
When you have Uncontrolled Component there is no reason to use value property and onChange function, because TextField Component treats defaultValue as initial and it creates local state variable uncontrolledValue which interacts like when you have value prop.
So, in your case you have Uncontrolled Component and value property is excess.
Uncontrolled Component:
<TextField
defaultValue="Hello"
onGetErrorMessage={value => {
if (value === 'Foo') {
return 'Foo is not valid value';
}
}}
/>
Controlled Component
const [inputValue, setInputValue] = useState('Hello');
<TextField
value={inputValue}
onChange={(_, newValue) => {
if(newValue || newValue === '') {
setInputValue(newValue)
}
}}
onGetErrorMessage={value => {
if (value === 'Foo') {
return 'Foo is not valid value';
}
}}
/>
How value and defaultValue properties affect the behavior of these two handlers(onChange and onGetErrorMessage)?
Answer lies in documentation:
defaultValue:
Default value of the text field. Only provide this if the text field is an uncontrolled component; otherwise, use the value property.
value:
Current value of the text field. Only provide this if the text field is a controlled component where you are maintaining its current state; otherwise, use the defaultValue property.
Full working example Codepen.

Related

Changing Autocomplete value using react-hook-form (Material-UI)

I have two Autocomplete fields, my goal is to change the value of the second field by the value of the first field.
The problem I face is when my trying to send the new value to the "setValue" function nothing happens the state form changing, but the autocomplete field shows the old value.
In this sand box:
https://codesandbox.io/s/dazzling-carson-84dxp?file=/src/Form/Components/UserCountry.js
you can see my implementation.
If you look at console when you change user_name field, you can see materialUI warning in the console which says:
Material-UI: A component is changing the uncontrolled value state of Autocomplete to be controlled.
it's reason is that your user_country's value by default is undefined and material consider this field as uncontrolled field, it means material will take the control of user_country value. To solve the problem you have two solutions:
1- Defining your form by defaultValues options like this:
const methods = useForm({
defaultValues: {
user_name: null,
user_country: null
}
});
2- Sending null as value to AutoComplete when it doesn't have any value, like this:
<Controller
control={control}
name={`user_country`}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="country-select-demo"
options={countries}
onChange={(event, item) => {
onChange(item);
}}
value={value || null}
...
/>
)}
/>
Here You can see your form which updates user_country whenever you change user_name field.

react-final-form input.onChange(null) does not update state immediately

We currently have a component <RenderDropdown /> which renders a material-ui <Select /> element. This takes options as props and is wrapped in a react-final-form <Field />.
Our issue is when we load the initialValues props of the <Form />, there are a few cases where the selected dropdown value no longer exists in the dropdown, so we want to set the dropdown value to null. We do that within the <RenderDropdown />:
componentDidMount() {
this.clearInputIfValueDoesntExistInOptions();
}
componentDidUpdate() {
this.clearInputIfValueDoesntExistInOptions();
}
clearInputIfValueDoesntExistInOptions() {
const {
input: { value, onChange },
options
} = this.props;
// Clear input value if it doen't exist in options
if (
value &&
!options.some((option) => option.key === value || option.value === value)
) {
console.log(
"clearInputIfValueDoesntExistInOptions",
"Setting value to null as the selected value does not exist in the current dropdown's options"
);
// If I use input.onChange, it does not update the final form state to null immediately.
onChange(null);
}
}
This does set the value to null, but the form state does not update immediately, so the UI does not update either. It only updates once we change another field value or if we submit the form.
I've made a quick demo that simulates our issue:
https://codesandbox.io/s/react-final-form-inputonchange-null-k7poj
Thanks.

event is null when using isClearable on react-select

I am passing through the react-select Select component as an InputComponent within the Material-UI InputBase component. I have successfully been able to populate the value from the options, however, I'm unable to use isClearable.
When isClearable is triggered, null is passed to the handleChange(event) function and I'm hoping there is a way to force an object through to prevent null creating an error.
The handleChange function within InputBase has var element = event.target || inputRef.current. As event is null, it's not even getting to inputRef which will contain the required object.
Would be good to get this working as an uncontrolled component.
I have created a codebox to illustrate the problem: https://codesandbox.io/s/morning-feather-l7xqf
You could supply your custom onChange() to catch the null and pass through your own value:
// Deconstruct from otherProps
const SelectWrapper = ({ inputRef, onChange, ...otherProps }) => {
function handleChange(event) {
// Overwrite the event with your own object if it doesn't exist
if (!event) {
event = {
target: inputRef,
value: '',
};
}
onChange(event);
}
return (
// Pass in the custom handleChange
<Select styles={customStyle} isClearable ref={inputRef} onChange={handleChange} {...otherProps} />
);
};

Can't change value of react-select field

I am using the React-Select library for my React app select fields. I have the same form for adding and editing data. If I don't provide any value, it works just fine, but if I want to have default values when the app renders, I can't change them. They appear as selected, but if I try to select another option it just doesn't update and the value I provided in value prop remains selected.
I tried using defaultValue instead of value, but then it doesn't select anything when the app renders.
<Select
placeholder="Deposit"
options={deposits}
getOptionValue={ option =>
option["id"]
}
getOptionLabel={ option => {
return option["name"];
}}
onChange={ value => {
this.setState({ deposit_id: value.id }}
value={{
value: deposit_id || "",
name: deposit_name || ""
}}
/>
deposits is an array of objects, and it renders the list of names, but when I click on one of them, the one that I provided in value remains selected, but it should select the one that I click on... How do I change it? Is there something wrong with my onChange function? Or what is it that I'm doing wrong?
Try the following code. The value props should be a string, i.e the value from the state.
<Select
placeholder="Deposit"
options={deposits}
getOptionValue={ option =>
option["id"]
}
getOptionLabel={ option => {
return option["name"];
}}
onChange={ value => {
this.setState({ deposit_id: value.id }}
value={deposit_id}
/>

Antd RadioGroup onChange fires before radio value is set

I'm using ant design and I'm having trouble with a radio group. It seems to me like the onChange handler on the RadioGroup fires when a radio button is clicked, but before the value of that radio button is set.
Here is some sample code
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log('field value', getFieldValue(name)); // previous value, not changed value.
};
const component =
getFieldDecorator &&
getFieldDecorator(name, {
initialValue: value,
rules: formatValidations(validations)
})(
<RadioGroup name={name} onChange={handleChange}>
<Radio value={1} key={1}>1</Radio>
<Radio value={2} key={2}>2</Radio>
<Radio value={3} key={3}>3</Radio>
</RadioGroup>
);
When this handleChange function runs, the field value is wrong. The first time I click radio 1, the value is undefined. Then I click 2, the value is 1. In other words, the onChange event is triggered, but the the value of the radio button is not yet set, I am only able to get the previously set value. Can anyone see anything wrong with my implementation?
Also, e.target.value is the expected changed value (it does not match the getFieldValue value).
Thanks
The antd support recommends using onValuesChange in Form.create() https://github.com/ant-design/ant-design/issues/20418
I found this workaround that works for me:
<Radio.Group
onChange={(e: RadioChangeEvent) => {
form.setFieldsValue({ yourRadioFieldNameFromGetFieldDecorator: e.target.value }, () => {
handleChange();
});
}}
>
The callback ensures that field value is set.
You should really use e.target.value if you want the value immediately. since the value is not set yet (to the antd form yet) at the moment you click the radio.(the field needs to pass setStatus, setfeedback etc)
if you really want to use getFieldValue, you can do the setTime strick
setTimeout(() => console.log(this.props.form.getFieldValue('radio-group')), 0)

Resources