Material UI Autocomplete: onChange is not triggered - javascript

In my example of Material UI's Autocomplete I want to select an option via keyboard events:
Browse through options with Up and Down arrow keys
Select desired option with ENTER
Unfortunately, the onChange is not triggered.
https://codesandbox.io/s/condescending-panna-xinkdw?file=/demo.tsx

Try using onClose, as when you navigate with your keyboard your state value is updated to whatever is highlighted, so if you wanna trigger an event when user press ENTER, just use onClose, and it gives you pretty much same props as onChange

This part in your causing onChange() not to trigger,
because of setInput(option):
const handleHighlightChange = (event, option, reason) => {
if (option && reason === "keyboard") {
setInput(option);
}
};
You can just call handleOnChange() withtout the using the prop onHighlightChange
for example:
export default function ComboBox() {
const [input, setInput] = React.useState(null);
const handleOnChange = (event, value, reason) => {
if (reason === "selectOption") {
window.location.href = value.url;
}
};
const handleFilterOptions = (currentOptions) => currentOptions;
return (
<Autocomplete
id="combo-box-demo"
value={input}
onChange={handleOnChange}
options={top100Films}
isOptionEqualToValue={(option, value) => option.label === value.label}
includeInputInList={true}
getOptionLabel={(option) => option.label}
filterOptions={handleFilterOptions}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}

Related

Material UI Autocomplete: highlighted option should become input value

When a user browses through the options using the arrow keys on his keyboard, I take the value from the highlighted option and use it as the current input value (input here has its own state).
Unfortunately, the highlighted state of the option is lost upon saving the title or value of the option as the input. Is there a way to not lose it?
Here is an example:
https://codesandbox.io/s/modern-rgb-5tew1p?file=/demo.tsx
P.S.:
Although this property sounded like it was made for it, includeInputInList does not help.
Thanks in advance!
Try this (I made some changes to your code like adding reason and isOptionEqualToValue):
export default function ComboBox() {
const [input, setInput] = React.useState(null);
const handleInputChange = (event, value) => {
setInput(value);
};
const handleHighlightChange = (event, option, reason) => {
if (option && reason === "keyboard") {
setInput(option);
}
};
const handleFilterOptions = (currentOptions) => currentOptions;
return (
<Autocomplete
id="combo-box-demo"
value={input}
onChange={handleInputChange}
options={top100Films}
isOptionEqualToValue={(option, value) => option.label === value.label}
includeInputInList={true}
onHighlightChange={handleHighlightChange}
getOptionLabel={(option) => option.label}
filterOptions={handleFilterOptions}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}

How to set Formik custom component value to Formik value

I'm using Formik for my form with google place auto-complete, I want to render places auto-complete as a custom component in the Formik field.
form.js
<Formik initialValues={location:""}>
<Field name="location" component={PlacesAutoComplete} placeholder="enter your location"/>
{...rest of form}
</Formik>
auto-complete component
import PlacesAutocomplete , {
geocodeByAddress,
geocodeByPlaceId
} from "react-google-places-autocomplete";
export const PlacesAutoComplete = ({
field: { name, ...field }, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
classes,
label,
...props
}: any) => {
const [fieldName, setFildName] = React.useState(field.name);
const [address, setAddress] = React.useState(props.value || "");
const error = errors[name];
// const touch = touched[name];
const handleSelect = () => {
// set this value to formik value
};
const handleChange = () => {
// set this value to formik value
};
const handleError = () => {
props.form.setFieldError(fieldName, error);
};
return (
<PlacesAutocomplete
value={address}
onChange={handleChange}
onSelect={handleSelect}
onError={handleError}
name={name}
placeholder={props.placeholder}
id={name}
{...props}
apiKey="Api key here"
>
{({
getInputProps,
suggestions,
getSuggestionItemProps,
loading
}: any) => (
<div>
<input
{...getInputProps({
placeholder: "Search Places ...",
className: "location-search-input form-control"
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map((suggestion: any) => {
const className = suggestion.active
? "suggestion-item--active"
: "suggestion-item";
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: "#fafafa", cursor: "pointer" }
: { backgroundColor: "#ffffff", cursor: "pointer" };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
);
};
How I set places auto-complete value to formik value, I'm pretty new to react and confused in handle change and on change functions. also, I found a solution in react class component here, But when converting those codes into functional components I'm stuck in Onchange and onSlecet functions
Better not write functional components as you'll get stuck with the test cases if you are writing.
OnChange is even you type anything, the value gets stored in onChange.
Abe onSelect is when you select anything
Basically on change you need to call formik's field onChange function. So in case you get an event on handleChange, just do this
const handleChange = (event) => {
// set this value to formik value
field.onChange(event.target.value)
};
or in case you get value in handleChange then do this
const handleChange = (value) => {
// set this value to formik value
field.onChange(value)
};
This will sync your formik state with autocomplete state.
Now comes the part for select. In this case also you can take the same route
const handleSelect = (value) => {
// set this value to formik value
field.onChange(value)
};
or you can use the setField function of form to update the value
const handleSelect = (value) => {
// set this value to formik value
form.setField('location',value)
};

Input unfocus after typing something in it, i put an onChange function that sets a state using setstate(react hooks) (TextField material ui)

When using a setting a state((useState() / setState()), the input unfocuses, idk why?
pls help
React hook State
const [username, setUsername] = useState("");
this is the my own component which returns a textfield but with my own styles
function FormInput({ onChange, ...rest }) {
const classes = formInputStyles();
return (
<div>
<TextField
onChange={(e) => onChange(e.target.value)}
InputProps={{ classes, disableUnderline: true }}
{...rest}
/>
</div>
);
}
Like, i said in the title whenever i type something in the input field it unfocuses and clears the input field.
It dosen't unfocus when i use the normal < TextField > from material ui.
i tried making a whole new function for setting state but that didnt work
<FormInput
onChange={(value) => {
setUsername(value);
}}
label="Username"
variant="filled"
></FormInput>

React autocomplete component, call endpoint every time a letter is typed

I am working with react and the component autocomplete of material-ui, I need help with the following problem.
In the examples of autocomplete I saw you need to have all elements of the list in the frontend to use the autocomplete, in my case I get the list from a web service and it could be huge, so instead of searching for the whole list I want that every time a letter is typed in the autocomplete it generates a search to the web service filtering names according to the input that is being written and with a max results of 10 elements. The endpoint of the webservice already has a filter property where you can pass the quantity of results you want and the letters you want of the name.The only thing that the autocomplete has to do is everytime you type a letter it hits the endpoint (filtering with the word that is being typed) and updates the list of elements of the autocomplete.
Right now I have the following code, the problem is that it searches the whole list when you click the autocomplete but when you type each letter it doesn't do anything.
import Autocomplete from '#material-ui/lab/Autocomplete';
import TextField from '#material-ui/core/TextField';
import CircularProgress from '#material-ui/core/CircularProgress';
const [open, setOpen] = React.useState(false);
const [organizationList, setOrganizationList] = React.useState([]);
const loading = open && organizationList.length === 0;
React.useEffect(() => {
let active = true;
if (!loading) {
return undefined;
}
(async () => {
if (active) {
try {
setOrganizationList(await api.post('/v1/organizations/search', {maxResults:10}));
} catch (error) {
snackbar.showMessage(error, "error");
}
}
})();
return () => {
active = false;
};
}, [loading]);
React.useEffect(() => {
if (!open) {
setOrganizationList([]);
}
}, [open]);
The definition of the autocomplete:
<Autocomplete
id="asynchronous-demo"
style={{ width: 300 }}
open={open}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
getOptionSelected={(option, value) => option.orgName === value.orgName}
getOptionLabel={(option) => option.orgName}
options={organizationList}
loading={loading}
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
variant="outlined"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
To hit the endpoint I have this:
setOrganizationList(await api.post('/v1/organizations/search', {maxResults:10}));
I need to pass the input of the autocomplete every time a letter is typed, like this:
setOrganizationList(await api.post('/v1/organizations/search', {name:inputAutocomplete,maxResults:10}));
Thanks a lot for the help.
Im new to react by the way.
In material-ui library Autocomplete component has a props onChange that can be used like this.
onChange={(event, newValue) => {
setValue(newValue);
}}
You should be interested in the second parameter newValue. Thus, you will receive a new input value every time a letter is typed.
Therefore, just move the logic for getting the list into this callback.
You can read more about controllable state in the material-ui documentation
Implementing the onChange function and giving it the function you already made should give you the solution you want.

Is it possible use react-codemirror2 with formik?

I have a project and I use Formik and react-codemirror2, I want control the onChange in Formik but the onChange in codemirror2 don't have event...and i dont't know how to use...
let me explain better :
i have a formink:
<Formik
enableReinitialize
onSubmit={values =>
{this.submitFormDefinition(values)}}
initialValues={this.state}
>
{({ handleSubmit }) => (
<div>
<Form
onSubmit={handleSubmit}
autoComplete="off"
onChange={event => {
this.handleChange(event)
}}
>
<Field
component={CustomInputComponent}
name="name"
id="id"
multiline
/>
</Form>
</div>
)}
where I have my function handleChange:
handleChange = event => {console.log('change') ....}
an where the CustomInputComponent is my codemirror2 component:
const CustomInputComponent = ({ field, form, ...props }) => {
return (
<CodeMirror
id={props.id}
name={props.name}
value={this.state.value}
options={{
mode: 'javascript',
theme: 'default',
lineNumbers: true,
}}
{...props}
onBeforeChange={(editor, data, value) => {
console.log({ value })
this.setState({ jsonSchema: value })
}}
onChange={(editor, data, value) => {
?????????????
}}
/>
)
}
If I use another component as textField from Materia-UI it work i don't need to call on my CustomInputComponent the onChange , that is direct call by formink... because also the onChange of textField have as parameter event... but as you can see in the code the onChange of codeMirror doesn't have event...
I need to call the my handleChange and not onChange of codeMirror ...
I tried to do something like that:
<CodeMirror
onChange=form.handleChange
or use :
<CodeMirror
onKeyUp={form.handleChange}
or:
<CodeMirror
onKeyUp={(editor, event) => {
form.handleChange(event)
}}
but nothing works
my function handleChange is never call
How use react-codeMirror2 with Formik? is possible? how can I itercept the onChange of Formink?????
Formik's handleChange method expects an HTMLInput change event, and it gets the updated value from the input. Unfortunately, you don't get that convenience if you're using CodeMirror. However, CodeMirror provides the value of the component onBeforeChange, and Formik provides a setFieldValue method, which lets you imperatively set a value into state. So you can do this:
<Formik {...whateverPropsYouSet}>
(formikProps) => (
<CodeMirror
value={formikProps.values.yourDataField}
onBeforeChange={(_editor, _data, value) => {
formikProps.setFieldValue('yourDataField', value);
}
/>
)
</Formik>

Categories