MUI: The `getOptionLabel` method of Autocomplete returned undefined instead of a string - reactjs

I am having trouble configuring this error (below), when I click the autocomplete search the display data is blank the error shows on the console
when i tried to type
the api fetch on when the user typing
where did i get wrong?
LocationSearch
const propTypes = {
getOptionLabel: PropTypes.func,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
}
const defaultProps = {
getOptionLabel: () => {},
value: '',
}
const LocationSearch = ({
getOptionLabel,
value,
})
....
<Autocomplete
getOptionLabel={getOptionLabel}
value={value}
/>
Parent
import {LocationSearch} from '../../component/LocationSearch'
......
<LocationSearch
getOptionLabel={(options) => options.title}
value={threatLocation}
/>
error

try providing an array of objects in the getOptionLabel parameter as autocomplete option maps data and find the key as you are looking for maybe that works.
<Autocomplete
getOptionLabel={[{title:'a', name: 'a'}, {title:'b', name: 'b'}]}
value={value}
/>

Related

How to get floatValue automatically from react-number-format inside a antd Form.Item?

I'm looking for a way to antd Form get automatically a floatValue from react-number-format.
`<Form.Item
label="My label"
name="fator"
>
<NumericFormat
thousandSeparator="."
decimalSeparator=","
decimalScale={2}
prefix="R$"
fixedDecimalScale={true}
onValueChange={(values, sourceInfo) => {
setState({
...state,
fator: values.floatValue!,
});
}}
/>
</Form.Item>`
Some people used onValueChange to get floatValue, but I do not know if it is the only way or the best way to get that value. On that way, we have to check each field to assign the correct value.
In my opinion, it is not the best way. It is so manually work.
const submit = async (values: stateModel) => {
values.fator = state.fator;
...
...
await doRegister(values);
..
}
How the best way to solve the problem?
I tried using some replacement on values returned from form submit, but it is not the best way to solve the problem.
You can solve this issue by creating a custom Number Field, which takes value and onChange as props.
const CustomNumberField = ({ value, onChange }) => {
return (
<NumericFormat
value={value}
thousandSeparator='.'
decimalSeparator=','
decimalScale={2}
prefix='R$'
fixedDecimalScale={true}
onValueChange={(values, sourceInfo) => {
onChange(values.floatValue);
}}
/>
);
};
Now the main Form will look like this:
const App = () => {
return (
<Form
onFinish={(values) => {
console.log(values); // { factor: 12 }
}}
>
<Form.Item label='My label' name='fator'>
<CustomNumberField />
</Form.Item>
{/* OR */}
{/* <Form.Item name='field'>
{(control, meta, form) => {
return <CustomNumberField {...control} />;
}}
</Form.Item> */}
<Button htmlType='submit'>Submit</Button>
</Form>
);
};
Q: Where that value & onChange prop come from?
When you pass name prop to Form.Item, the field inside the Form.Item is now controlled by Form. You can either pass a ReactElement or a function. For ReactElement, it pass two props value & onChange. For callback function, it looks like this:
children?: React.ReactElement | ((control: ChildProps, meta: Meta, form: FormInstance<Values>) => React.ReactNode);
interface ChildProps {
[name: string]: any;
}
interface Meta {
touched: boolean;
validating: boolean;
errors: string[];
warnings: string[];
name: InternalNamePath;
}
type InternalNamePath = (string | number)[];

how to refresh the antd pro ProFormText initialValue

I am using antd pro to develop an app, now facing a problem is that the ProFormText initialValue did not update when the props changed. I pass the record from props and give it to the ModalForm, this is the code looks like:
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const intl = useIntl();
const { initialState } = useModel('##initialState');
return (
<ModalForm
title={intl.formatMessage({
id: 'pages.apps.jobs.interview.updateInterview',
defaultMessage: 'New rule',
})}
width="400px"
visible={props.updateModalVisible}
onVisibleChange={(value)=>{
if(!value){
props.onCancel();
}
}}
onFinish={props.onSubmit}
>
<ProFormText
initialValue={props.values.company}
name="company"
label={intl.formatMessage({
id: 'pages.apps.jobs.interview.searchTable.company',
defaultMessage: 'company',
})}
width="md"
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.searchTable.updateForm.ruleName.nameRules"
defaultMessage="Please input the name!"
/>
),
},
]}
/>
);
}
when open the modal and give the initial value, the next time when the props value change, the ProFormText still keep the first time value. I have read this question: Update antd form if initialValue is changed seems it only works on antd. the ModalForm did not contain the useForm() method. what should I do to fix this problem and keep the value changed follow props? This is the version info:
"#ant-design/pro-form": "^1.52.0",
"#ant-design/pro-layout": "^6.32.0",
"#ant-design/pro-table": "^2.61.0",
I am facing the same problem with you, and tried follow the Update antd form if initialValue is changed instructions and works. First add:
const [form] = Form.useForm()
and bind the form with ModalForm like this:
<ModalForm
form = {form}
title={intl.formatMessage({
id: 'pages.apps.jobs.interview.updateInterview',
defaultMessage: 'New rule',
})}
width="400px"
visible={props.updateModalVisible}
onVisibleChange={(value)=>{
if(!value){
props.onCancel();
}
}}
onFinish={props.onSubmit}
>
you may facing the error Module "./antd/es/form/style" does not exist in container, just delete the .umi cache folder and rebuild the project. Finally add this code to reset the fields when the props changed:
useEffect(() => {
form.resetFields();
form.setFieldsValue(props.values);
});

A value coming from Autocomplete is set to undefined, why?

I'm using a form that I send the data to function (submitHandler), and in the submitHandler function I get the data by referencing it to the form fields.
After it I bring it to the console and see the data there:
import { useRef } from "react";
function NewForm() {
const citiesRef = useRef();
const typeRef = useRef();
...
function submitHandler(event) {
event.preventDefault();
const enteredCities = citiesRef.current.value;
const enteredType = typeRef.current.value;
const printing = {cities: enteredCities, type: enteredType);
console.log(printing);
}
return (
<form onSubmit={submitHandler}>
...
<input type="text" id="cities" ref={citiesRef} />
<input type="text" id="type" ref={typeRef} />
....
Until now everything is working well.
When I start using the MUI framework and its Autocomplete component, the data that I get from the Autocomplete (using the console.log), is set as undefined.
instead of using the normal input text field, I wrote this:
<Autocomplete disablePortal id="cities" options={citiesList}
renderInput={(params) => <TextField {...params} label="" />} ref={citiesRef} />
...
const citiesList = [
{ cityId: "472", label: "Name1 ",},
{ cityId: "473", label: "Name2 ",},]
Now the output in the console.log shows 'undefined'.
Does anyone have an idea why?
Note:
(When I try to directly send to console.log by using the onChange props, from the Autocomplete component:
onChange={(event, value) => console.log(value)} Works, and presents the correct array:
(2) [{…}, {…}]
0: cityId: "472" label: "Name1 " [[Prototype]]: Object
1: cityId: "473" label: "Name2 " [[Prototype]]: Object
length: 2 [[Prototype]]: Array(0)
).

Material UI - Autocomplete with React Hook Form

I am building a form using Material UI's Autocomplete feature and using recoil for state management. I am also using react-hook-form. I am needing the following criteria met:
Need typeahead that allows first letter typed to return a list of options from an API to show in Autocomplete. Each letter will return a different list of options to select from.
Need to also allow freeSolo so that user can enter a value manually
Needs to be required and follow a pattern to validate the form.
I am using react-hook-form's <Controller> to control the input and allow for features like validation, displaying error messages in the helper text, etc.
The Problems: I am having issues with the typeahead filtering out options based on what I type, along with allowing freeSolo. As I type a new value, the list of options do not filter. The popup just stays open. I also need to validate on change of input for the pattern validation. I have tried with the following example with onInputChange to make use of react-hook-form's useForm and setValue to manually set the value of the field and validate the form. ({shouldValidate: true}). The below example is a custom, reusable component I created for Autocomplete, as well as using that custom component in other parent components. I hope I included as much details as possilbe, but if not, please let me know if you need anything more. Any assistance would be very appreciative!
Parent Component:
const setTrainType = useSetRecoilState(TrainType)
// Trains would return a list of trains based on letter of train type that was passed from input
const trainsList = useRecoilValue(Trains)
const trainOptions = useMemo(() => trainsList.map(trainIDFormatted), [
trainsList,
])
const handleInputChange = useCallback(
(_e: unknown, option: string, reason: string) => {
const capitalized =
option === capitalize(option) ? option : capitalize(option)
setValue('trainID', capitalized, {shouldValidate: true})
if (['input', 'reset'].includes(reason) && capitalized !== '') {
setTrainType(capitalized.charAt(0))
} else {
setTrainType(undefined)
}
},
[setTrainType, setValue],
)
<Autocomplete
autoSelect
freeSolo
disabled={disabled}
helperText=" "
label="Select a train"
name="trainID"
options={trainOptions}
rules={{
pattern: {
message: 'Must match train ID pattern',
value: /^(?:[A-Z]-?[A-Z ]{6}-?[0-9 ]-?[0-9 ]{2}[A-Z ])?$/,
},
required: 'Train is required',
}}
onInputChange={handleInputChange}
/>
Custom autocomplete component:
import {
AutocompleteProps,
Autocomplete as MuiAutocomplete,
} from '#material-ui/lab'
import {get} from 'lodash'
import React, {ReactNode, useCallback} from 'react'
import {
Controller,
ControllerProps,
FieldError,
useFormContext,
} from 'react-hook-form'
import {useRenderInput} from './hooks'
interface Props
extends Pick<ControllerProps<'select'>, 'rules'>,
Omit<
AutocompleteProps<string, false, false, true>,
'error' | 'onChange' | 'required' | 'renderInput'
> {
helperText?: ReactNode
label?: string
name: string
}
/**
* Render controlled autocomplete. Use react-form-hook's FormProvider.
* #param props Component properties
* #param props.helperText Default helper text for error
* #param props.label Input label
* #param props.name Name identifier for react-hook-form
* #param props.required If true then item is required
* #param props.rules Select rules
* #return React component
*/
export const Autocomplete = ({
helperText,
label,
name,
rules,
...props
}: Props) => {
// eslint-disable-next-line #typescript-eslint/unbound-method
const {control, errors, watch} = useFormContext()
const error: FieldError | undefined = get(errors, name)
const required = get(rules, 'required') !== undefined
const value = watch(name)
const renderAutocompleteInput = useRenderInput({
error: error !== undefined,
helperText: get(error, 'message', helperText),
label,
required,
})
const handleOnChange = useCallback(
(_e: unknown, option: string | null) => option,
[],
)
const renderAutocomplete = useCallback(
params => (
<MuiAutocomplete
{...props}
{...params}
renderInput={renderAutocompleteInput}
onChange={handleOnChange}
/>
),
[handleOnChange, props, renderAutocompleteInput],
)
return (
<Controller
control={control}
defaultValue={value ?? ''}
name={name}
render={renderAutocomplete}
rules={rules}
/>
)
}
What it looks like:

how to read the children function properties in jest or enzyme

This is piece of my react component code:
<div style={{ width }}>
<FormField
label='Select a type'
labelPlacement='top'
className='cdsdrop'
>
{({ getButtonProps }) => (
<Dropdown
{...getButtonProps({
id: 'typeDropDown',
source: data,
onChange: this.handleInputChange,
options: data
})}
/>)}
</FormField>
</div>
Am new to jest framework. I started writing testcases for submit button and reset are disabled when dropdown value is empty, after selecting dropdown buttons should get enable.
When I use props().label am getting label but when I called children am getting error.
this is mytest component
describe('Buttons should be disabled on page load', () => {
it('submit and reset buttons are disabled when type is empty', () => {
const wrapper = shallow(<CdsNettingActions/>);
const submitButton = wrapper.find('WithStyles(Component).cdssubmit');
const resetButton = wrapper.find('WithStyles(Component).cdsreset');
const dropDown = wrapper.find('WithStyles(Component).cdsdrop');
const drop1=dropDown.props().children();
console.log('drop',drop1);
expect(submitButton.prop('disabled')).toEqual(true);
expect(resetButton.prop('disabled')).toEqual(true);
});
});
But am getting below error:
TypeError: Cannot read property 'getButtonProps' of undefined
className='cdsdrop'>
When I did the console logging the children function looks as below:
getButtonProps({
id: 'typeDropDown',
borderless: true,
buttonWidth: width,
source: data,
onChange: _this4.handleInputChange,
options: data
}))
Please help me how to read options from the dropdown.
I am using shollow strong textreact 16
So your FormField's children prop is a callback that expects an object with getButtonProps property:
{({ getButtonProps }) => (
That's why when you just do
const drop1=dropDown.props().children();
it crashes - there is no object with getButtonProps. You may pass this argument, but next you will find drop1 variable contains React object, not Enzyme's ShallowWrapper. So any checks like expect(drop1.prop('something')).toEqual(2) will fail "prop is not a function".
So you either use renderProp():
const drop1 = dropDown.renderProp('children')({
getButtonProps: () => ({
id: 'typeDropDown',
borderless: true,
buttonWidth: someWidth,
source: mockedSource,
onChange: mockedOnChange,
options: mockedOptions
})
});
Or maybe it's much easier to use mount() instead.

Resources