Issue with async default value in React Material UI Autocomplete - javascript

I am using 'Material UI' Autocomplete component to render a dropdown in my form. However, in case the user wants to edit an object then the dropdown should be displayed as autofilled with whatever value that's being fetched from the database.
I've tried to mock the situation using below code
import React, { Component } from 'react';
import Autocomplete from '#material-ui/lab/Autocomplete';
import TextField from '#material-ui/core/TextField';
export default class Sizes extends Component {
state = {
default: []
}
componentDidMount() {
setTimeout(() => {
this.setState({ default: [...this.state.default, top100Films[37]]})
})
}
render() {
return (
<Autocomplete
id="size-small-standard"
size="small"
options={top100Films}
getOptionLabel={option => option.title}
defaultValue={this.state.default}
renderInput={params => (
<TextField
{...params}
variant="standard"
label="Size small"
placeholder="Favorites"
fullWidth
/>
)}
/>
);
}
}
Here after the component is mounted, I'm setting a timeout and returning the default value that should be displayed in the dropdown
However, it's unable to display the value in the dropdown and I'm seeing this error in console -
index.js:1375 Material-UI: the `getOptionLabel` method of useAutocomplete do not handle the options correctly.
The component expect a string but received undefined.
For the input option: [], `getOptionLabel` returns: undefined.
Apparently the state is getting updated when componentDidMount is getting called but the Autocomplete component's defaultValue prop is unable to read the same
Any idea what I might be getting wrong here?
Code sandbox link - https://codesandbox.io/s/dazzling-dirac-scxpr?fontsize=14&hidenavigation=1&theme=dark

For anyone else that comes across this, the solution to just use value rather than defaultValue is not sufficient. As soon as the Autocomplete loses focus it will revert back to the original value.
Manually setting the state will work however:
https://codesandbox.io/s/elegant-leavitt-v2i0h

Following reasons where your code went wrong:
1] defaultValue takes the value which has to be selected by default, an array shouldn't be passed to this prop.
2] Until your autocomplete is not multiple, an array can't be passed to the value or inputValue prop.

Ok I was actually able to make this work. Turns out I was using the wrong prop. I just changed defaultValue to value and it worked.
Updated code pen link - codesandbox.io/s/dazzling-dirac-scxpr

Related

I want to clear input datepicker value on clicking clear button in react

When the value is set using defaultValue prop it is clearing the field but, not when used value prop. package details import { TextField} from '#material-ui/core';
The issues is caused because of the required property in the component, I removed it and it started working fine. check this code codesandbox, try adding and removing required property and click on clear button
Provided you do not have the control marked required, the onChange should fire on clear, handing you an empty string.
We also need to handle that case properly. Testing the date for validity and rejecting invalid dates by setting it to null perhaps.
With that change, make sure formatDateAndTime correctly handles the case in which the date is null, and so returns an empty string.
<TextField
type="datetime-local"
value={formatDateAndTime(this.state.editedValue.startDateTime) }
onChange={(date: any) => {
const selectedDate = new Date(date.target.value);
const sanitizedDate = isNaN(selectedDate) ? null : selectedDate;
this.setState((prevState) => ({
editedValue: { ...prevState.editedValue, startDateTime: sanitizedDate }
}));
}}
/>

Why does custom input component cause "Function components cannot be given refs" warning?

While trying to customize the input component via MUI's InputUnstyled component (or any other unstyled component, e.g. SwitchUnstyled, SelectUnstyled etc.), I get the warning
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of `ForwardRef`.
InputElement#http://localhost:3000/main.4c2d885b9953394bb5ec.hot-update.js:59:45
div
...
I use the components prop to define a custom Input element in my own MyStyledInput component which wraps MUIs InputUnstyled:
import InputUnstyled, {
InputUnstyledProps
} from '#mui/base/InputUnstyled';
const MyStyledInput: React.FC<InputUnstyledProps> = props => {
const { components, ...otherProps } = props;
return (
<InputUnstyled
components={{
Input: InputElement,
...components,
}}
{...otherProps}
/>
);
};
My custom input component InputElement which is causing the Function components cannot be given refs warning:
import {
InputUnstyledInputSlotProps,
} from '#mui/base/InputUnstyled';
import { Box, BoxProps } from '#mui/material';
const InputElement: React.FC<BoxProps & InputUnstyledInputSlotProps> = (
{ ownerState, ...props }
) => {
return (
<Box
component="input"
// any style customizations
{...props}
ref={ref}
/>
);
});
Note: I'm using component="input to make MUI's Box component not render an HTML div but an HTML input component in the DOM.
Why am I getting this warning?
Other related questions here, here and here address similar issues but don't work with MUI Unstyled components. These threads also don't explain why
The warning wants you to have a look at the InputElement component. To be honest, the stack-trace is a bit misleading here. It says:
Check the render method of ForwardRef.
InputElement#http://localhost:3000/main.4c2d885b9953394bb5ec.hot-update.js:59:45
div
You can ignore the ForwardRef here. Internally InputElement is wrapped by
The crucial part for understanding this warning is:
Function components cannot be given refs. Attempts to access this ref will fail.
That is, if someone tries to access the actual HTML input element in the DOM via a ref (which Material UI actually tries to do), it will not succeed because the functional component InputElement is not able to pass that ref on to the input element (here created via a MUI Box component).
Hence, the warning continues with:
Did you mean to use React.forwardRef()?
This proposes the solution to wrap your function component with React.forwardRef. forwardRef gives you the possibility to get hold of the ref and pass it on to the actual input component (which in this case is the Box component with the prop component="input"). It should look as such:
import {
InputUnstyledInputSlotProps,
} from '#mui/base/InputUnstyled';
import { Box, BoxProps } from '#mui/material';
const InputElement = React.forwardRef<
HTMLInputElement,
BoxProps & InputUnstyledInputSlotProps
>(({ ownerState, ...props }, ref) => {
const theme = useTheme();
return (
<Box
component="input"
// any style customizations
{...props}
ref={ref}
/>
);
});
Why do I have to deal with the ref in the first place?
In case of an HTML input element, there as a high probability you want to access it's DOM node via a ref. This is the case if you use your React input component as an uncontrolled component. An uncontrolled input component holds its state (i.e. whatever the user enters in that input field) in the actual DOM node and not inside of the state value of a React.useState hook. If you control the input value via a React.useState hook, you're using the input as a controlled component.
Note: An input with type="file" is always an uncontrolled component. This is explained in the React docs section about the file input tag.

React - MUI TextField error does not visibly display after error state is changed

I am trying to validate an email TextField with a button. I am using props to store the email TextField's error state.
Email Textfield code:
<TextField
required
error={values.emailInvalid}
id="emailAddress"
name="emailAddress"
label="Email Address"
onChange={handleChange("email")}
defaultValue={values.email}
fullWidth
autoComplete=""
variant="standard"
/>
Button code:
var re = /\S+#\S+\.\S+/;
var testEmail = re.test(this.props.values.email);
if (this.props.values.email.length === 0 || !testEmail) {
this.props.values.emailInvalid = true;
} else {
this.props.values.emailInvalid = false;
//go next page
}
console.log(this.props.values.emailInvalid);
The "emailInvalid" boolean is updating properly according to the console output, but the TextField does not visibly show the red error state is "emailInvalid" is set to true.
You cannot just assign the value of emailInvalid with this.props.values.emailInvalid = true.
If values is a state object, you need to also pass the setState function in props and use it like below
this.props.setState({ emailInvalid:true })
Also, you should use this.props.values instead of values in the TextField component
You need to use setState to mutate the component state for example
SetEmailInvalid(true);
for Class based components:
this.setState({emailInvalid: true});
You can't edit react state directly. Open This Link To See Why
The way you are changing things in React is not supported. you can follow this guide:
First of all: PROPS ARE READONLY and you can not change them.
Second, for changing things that have to be shown in the browser as it changes is done via State. to fix your error first import useState like: (we are using states as hook)
import {useState} from "react";
then you have to use your state and initialize the amount:
const [emailIsValid, setEmailIsValid] = useState(true)
this will set the initial value of the emailIsValid to true.
now whenever you want to change the value of emailIsValid , you have to call the set function and set the value like:
setEmailIsValid(false)
now you can use the emailIsValid anywhere in your react component.

MUI - React does not recognize the `renderInput` prop on a DOM element in Datepicker

I am trying to use the DatePicker component from MUI version 5. I have included it exactly as it is specified in the codesandbox example from the MUI docs. So my component looks like this:
const MonthPicker: FC = () => {
const [value, setValue] = React.useState<Date | null>(new Date());
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
views={['year', 'month']}
label="Year and Month"
minDate={new Date('2012-03-01')}
maxDate={new Date('2023-06-01')}
value={value}
onChange={(newValue) => {
setValue(newValue);
}}
renderInput={(props) => <TextField {...props} size='small' helperText={null} />}
/>
</LocalizationProvider>
)
}
No matter what I tried, I always got the error message: React does not recognize the renderInput prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase renderinput instead.
If I write it in lowercase, then the code doesn't even render. What am I doing wrong here?
This is a bug from MUI, you can see it happens in the official docs too when you open the MonthPicker. It's because they forgot to filter the renderInput callback before passing the rest of the props to the DOM element.
You can see from the source that the YearPicker doesn't have this problem because it passes every props down manually, while the MonthPicker chooses to spread the remaining props which includes renderInput - this is an invalid prop because the HTML attribute doesn't know anything about JS callback object.
This error is just a warning from ReactJS when it thinks you're doing something wrong, but you're not because this is an upstream bug, and it doesn't affect anything else functionality-wise, so ignore it.

How do I get inputRef to return <input> element from <Textfield>? Tried everything I can think of and it's still null

I have a TextField that I only render when another radiobutton is clicked. According to the React and Material-UI documentation, I should be able to get a ref to an input element inside a Mui TextField using inputRef={this.myRef} on the TextField. I'm able to do this for another TextField that is not toggled, but when I try that the with TextField that I just turn on, it shows null.
I've tried using inputRef={this.otherText} and inputProps={{ref: this.otherText}} and same result.
// Start of my class where I do the createRef()
class Form extends Component {
constructor(props) {
super(props);
this.otherText = React.createRef();
}
// Start of function where I try to reference the ref:
processApplication = e => {
if (e.target.value.toLowerCase() === 'other') { // this triggers the TextField to be rendered
console.log(this.otherText); // Returns null, kinda - see screenshot
}
// The TextField I'm trying to reference:
<TextField
id='applicationOther'
name='applicationOther'
label='Describe application:'
margin='normal'
multiline={true}
fullWidth={true}
onChange={this.anyChange}
autoFocus={true}
inputRef={this.otherText} // Here's where the reference is made
/>
I expect this.otherText to have a reference to the element, but this.otherText.current is null.
So to add some content to an input of any tipe, including Material TextField, you'd assign it as a value, for instance:
this.state = { input: 'Some string' }
<TextField value={this.state.input} />
Keep in mind the slight difference between uncontrolled and controlled components, so depending on your use case, you may want to pass defaultValue instead of value. From the docs:
In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.
Docs link
I have had same issue, doing following worked for me:
<TextField
variant="filled"
inputRef={(input) => {
if(input != null) {
input.focus();
}
}}
/>
The trick here was if(input != null) in my case. You can also take a look at working example here: CodeSandBox- Material-ui-TextFieldFocus

Categories