I'm using React 17 and tried to use different chip input fields like material-ui-chip-input etc. Unfortunately, all npm packages I tried do not work with react version 17.
Do you know of any component that works with this version or how I could create one myself?
Thanks in advance
Edit:
I have managed to do what I wanted, but unfortunately, I now get the following error when pressing enter / adding a new item:
index.js:1 Warning: Cannot update a component (CreatePoll) while rendering a different component (ForwardRef(Autocomplete))
Here is the codesandbox which shows the problem:
https://codesandbox.io/s/compassionate-greider-wr9wq?file=/src/App.tsx
why don't you use this instead of a new package?
Autocomplete exactly do that.
const [receivers, setReceivers] = React.useState<string[]>([]);
import Autocomplete from '#mui/material/Autocomplete';
<Autocomplete
multiple
onChange={(e, value) => setReceivers((state) => value)}
id="tags-filled"
options={top100Films.map((option) => option.title)}
defaultValue={[top100Films[13].title]}
freeSolo
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip variant="outlined" label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
label="freeSolo"
placeholder="Favorites"
/>
)}
/>
import React from 'react'
import { MuiChipsInput } from 'mui-chips-input'
const MyComponent = () => {
const [chips, setChips] = React.useState([])
const handleChange = (newChips) => {
setChips(newChips)
}
return (
<MuiChipsInput value={chips} onChange={handleChange} />
)
}
Source:
https://github.com/viclafouch/mui-chips-input
Supports both React 17 / 18 and MUI V5
Use the freeSolo prop of Autocomplete, and just set the options prop to an empty array:
<Autocomplete
multiple
freeSolo
options={[]}
renderTags={(value, getTagProps) =>
value.map(option, index) => (
<Chip label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Add Chip"
placeholder="Type and press enter"
/>
)}
/>
Related
I'm using Material UI Autocomplete to get searched data, it works fine, but it's re-render after the search runs and clears the value without focus, then we have to click again to get fetched value.
here Is my auto-complete component
<AutoCompleteNewDesign
disableClearable
freeSolo
id="combo-box-demo"
onChange={(e, selectValue) => {
setInvoiceSelectedData(selectValue);
}}
options={invoiceSearchList}
getOptionLabel={option => (option ? String(option?.data1) : '')}
renderOption={option => (
<div>
{option.data1} - {option.data2}
</div>
)}
renderInput={params => (
<TextField
params={params}
name="name-here"
variant="outlined"
onChange={e => {
dispatch(
searchName(e.target.value);
}}
/>
)}
filterOptions={filterOptions}
/>
I used this reducer to store data which fecth when user searched
const { searchedNames } = useSelector(state => state.searchReducer);
How to call my dispatch asynchronous as well as prevent this re-render
I'm trying to autocomplete the input when searched with either of the two values - title and year. However, it does only work when I search with title. When I search with year, it does not show any options.
Sample code
export default function ComboBox() {
return (
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionLabel={(option) => option.title || option.year}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}
I have created a working example using Code Sandbox
Could anyone please help?
One way to make this work is to make the year part of the option label:
/* eslint-disable no-use-before-define */
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { top100Films } from "./movies";
export default function ComboBox() {
return (
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionLabel={(option) => `${option.title} (${option.year})`}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}
If you don't want to display the year, but still want to match on it, the other way is to use the filterOptions prop to customize the matching:
/* eslint-disable no-use-before-define */
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { createFilterOptions } from "#material-ui/lab/Autocomplete";
import { top100Films } from "./movies";
const filterOptions = createFilterOptions({
stringify: (option) => `${option.title} ${option.year}`
});
export default function ComboBox() {
return (
<Autocomplete
id="combo-box-demo"
filterOptions={filterOptions}
options={top100Films}
getOptionLabel={(option) => option.title}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
);
}
Related documentation: https://material-ui.com/components/autocomplete/#custom-filter
I wish I could do such a thing using Autocomplete of material ui: wertarbyte
That is, inserting text (string) without having a list of elements from which you must select.
Therefore the noOptions message should not appear, every time the enter key is pressed on the keyboard the text is inserted.
Link: codesandbox
Code:
import React from "react";
import Chip from "#material-ui/core/Chip";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
const useStyles = makeStyles(theme => ({
root: {
width: 500,
"& > * + *": {
marginTop: theme.spacing(3)
}
}
}));
export default function Tags() {
const classes = useStyles();
return (
<div className={classes.root}>
<Autocomplete
multiple
id="tags-outlined"
options={[]}
defaultValue={["foo", "bar"]}
//getOptionLabel={(option) => option}
//defaultValue={[top100Films[13]]}
//filterSelectedOptions
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
</div>
);
}
In case you have simple elements (not objects, just strings), and you don't really need to handle state (your autocomplete is not controlled) you can use the freeSolo prop of the Autocomplete.
<Autocomplete
multiple
freeSolo
id="tags-outlined"
options={["foo", "bar"]}
defaultValue={["foo", "bar"]}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
In case your elements are more complex and you do need to control the element:
Make sure the Autocomplete tag is a controlled one (you manage to value).
Listen to key down event on the TextField.
If the code is Enter (e.code === 'Enter') - take the value of the input and push it to the list of the current values that you have.
Make sure you also handle the onChange to handle the removal of elements:
Here is the code:
const [autoCompleteValue, setAutoCompleteValue] = useState(["foo", "bar"]);
return (
<Autocomplete
multiple
id="tags-outlined"
options={[]}
value={autoCompleteValue}
onChange={(e, newval, reason) => {
setAutoCompleteValue(newval);
}}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
onKeyDown={e => {
if (e.code === 'enter' && e.target.value) {
setAutoCompleteValue(autoCompleteValue.concat(e.target.value));
}
}}
/>
)}
/>
);
Check the live working example of both options: https://codesandbox.io/s/mui-autocomplete-create-options-on-enter-gw1jc
For anyone who wants to input the current best match on enter key press (as opposed to any custom text) you can use the autoHighlight prop.
<Autocomplete
multiple
autoHighlight
id="tags-outlined"
options={["foo", "bar"]}
defaultValue={["foo", "bar"]}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="filterSelectedOptions"
placeholder="Favorites"
/>
)}
/>
To do this, don't use the Autocomplete element from MUI. Just use a a standard TextField with the use of InputProps. All you need to do is add a onKeyDown listener to the TextField that listens for 'Enter' and when the function is triggered, have it add to an array of Chips in the InputProps. It might look something like this:
const [inputValue, setInputValue] = useState('');
const [chips, setChips] = useState([])
const inputChange = ({target: {value}}) => {setInputValue(value)};
const handleKeyDown = ({key}) => {
if(key === 'Enter') {
setChips([...chips, inputValue])
}
};
<TextField
fullWidth
variant="outlined"
label="Fish and Chips"
value={inputValue}
onChange={inputChange}
multiline
InputProps={{
startAdornment: chips.map((item) => (
<Chip
key={item}
label={item}
/>
)),
}}
/>
This is untested as written here, but it should work. I've done something similar in one of my apps.
folks
So my issue is simple. I want to provide custom closeIconButton. And only closeIcon prop is available.
CloseIcon prop doesn't suffice because I need this custom button to have onClick property.
And if I place onClick on CloseIcon material-ui will warn: Failed prop type: Material-UI: you are providing an onClick event listener to a child of a button element.
Firefox will never trigger the event.
<Autocomplete
open={open}
classes={classes}
options={practicesList}
getOptionLabel={get('name')}
value={curPractice}
blurOnSelect
closeIcon={<CloseIcon onClick={() => onChange(null)} />}
onChange={async (e, option) => {
if (!option) return
onChange(option.id)
}}
renderInput={params => <TextField {...params} autoFocus={autoFocus} fullWidth label={label} margin="none" />}
renderOption={(practice, { inputValue }) => {
const matches = match(practice.name, inputValue)
const letters = parse(practice.name, matches)
return (
<div>
{letters.map((letter, i) => (
<span key={i} style={{ fontWeight: letter.highlight ? 700 : 400 }}>
{letter.text}
</span>
))}
</div>
)
}}
/>
From api docs closeIcon should be just some node. For example:
<CloseIcon fontSize="small" />
Than, you can use onClose prop directly on Autocomplete component to specify some callback. Hope this helps.
I am referring to the documentation of React Material-UI (https://material-ui.com/components/autocomplete/).
In the demo code,
<Autocomplete
options={top100Films}
getOptionLabel={(option: FilmOptionType) => option.title}
style={{ width: 300 }}
renderInput={params => (
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
)}
/>
I get how it works, but I am not sure how I am supposed to get the selected value.
For example, I want to use the onChange prop to this so that I can make some actions based on the selection.
I tried adding onChange={v => console.log(v)}
but the v does not show anything related to the selected value.
Solved by using passing in the (event, value) to the onChange props.
<Autocomplete
onChange={(event, value) => console.log(value)} // prints the selected value
renderInput={params => (
<TextField {...params} label="Label" variant="outlined" fullWidth />
)}
/>
The onChange prop works for multiple autocomplete values as well (#Steve Angello #ShwetaJ). The value returned is a list of all the selected options.
const [selectedOptions, setSelectedOptions] = useState([]);
const handleChange = (event, value) => setSelectedOptions(value);
const handleSubmit = () => console.log(selectedOptions);
.
.
.
<Autocomplete
multiple
autoHighlight
id="tags-outlined"
options={top100Films}
getOptionLabel={(option) => option.title}
onChange={handleChange}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="standard"
/>
)}
/>
.
.
.
<button onClick={handleSubmit}>Submit!</button>
You can use useState to store the recevied value and onChange to get the value:
const [selected, setSelected] = useState([]);
return (
<Autocomplete
onChange={(event, value) => setSelected(value)}
renderInput={(params) => <TextField {...params} label="selected" />}
/>
);
Here is TS working example
const tags = ["perimeter", "Algebra", "equation", "square root"];
const handleInput = (e: React.SyntheticEvent, value: string[]) => {
console.log(value);
};
<Autocomplete
multiple
options={tags}
onChange={handleInput}
renderTags={(value: readonly string[], getTagProps) =>
value.map((option: string, index: number) => (
<Chip
variant="outlined"
label={option}
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
label="Tags"
placeholder="select question tags"
/>
)}
/>
============ Output ===============