How to map Fields select option in redux form? - reactjs

I tried to map an array so that I will not code a 200 codes in one particular select. This is my code.
const location = [ {id: 'A'}, ...... up to 200 id: values]
<FormControl fullWidth={true}>
<Field name='location'
component={renderSelectField}
option={location.map((location, index) => (<option value={location.id}>{location.id}</option>))}
props={{size: 'small',
type: 'text',
variant: 'outlined'
}}
/>
</FormControl>
const renderSelectField = ({input, label, meta: {touched, error}, children}) => (
<Select
floatinglabeltext={label}
errortext={touched && error ? 1 : 0}
{...input}
onChange={(value) => input.onChange(value)}
children={children}/>
);
instead of coding these:
{/* <option/>*/}
{/* <option value='A'>A</option>*/}
{/* <option value='B'>B</option>*/}
..... up to 200
I tried also put a return in it but does not work.

You have to map it using MenuItem instead of Option in Field element
{Location.map((location, index) => (
<MenuItem key={index} value={location.id}>
{location.id}
</MenuItem>
))}

Related

MUI Component Error Each child in a list should have a unique "key" prop

I was spending too much time finding answers to this error.
Have been already added key key={models.model_id} on that map function.
On my form:
<JSelect
label="Model"
labelId="assembly_model_id"
name="product_model_id"
value={values.product_model_id}
onBlur={handleBlur}
onChange={handleChange}
errors={errors}
>
{models.map((model) => {
return (
<MenuItem key={models.model_id} value={model.model_id}>
{model.model_code}
</MenuItem>
);
})}
</JSelect>
On my JSelect functional component:
const JSelect = ({ children, value, id, label, labelId, name, placeholder, type, onChange, onBlur, size, rows }) => {
return (
<FormControl fullWidth size={size ? size : 'small'}>
<InputLabel id={labelId}>{label}</InputLabel>
<Select labelId={labelId} id={id ? id : name} name={name} value={value} label={label} onBlur={onBlur} onChange={onChange}>
{children}
</Select>
</FormControl>
);
};
Error screenshots:
{models. Map((model,index) => {
return (
{model.model_code}
);
})}
add index next to model and assign this to key. check above code

Material UI Select Multiple Selection in Array

I created a Select using React that allows the user to select multiple options.
The problem is that the Select displays the ID of the selected item, instead of their name.
How can I change the code in a way that the Select display the names separated by commas (now shows the IDs separated by commas), while keeping the array of ids for later processing.
Any idea how to fix it? Here is the code to CodeSanbox
I have the following array in a Material UI Select:
const names = [
{ id: "1", value: "Oliver Hansen" },
{ id: "2", value: "Van Henry" },
{ id: "3", value: "Omar Alexander" }
];
This is the code that renders the Multiple Select:
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(", ")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name.id}>
<Checkbox checked={personName.indexOf(name.id) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
I found one possible solution for your issue.
check if it works for you.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Input, OutlinedInput } from "#material-ui/core";
import InputLabel from "#material-ui/core/InputLabel";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
import ListItemText from "#material-ui/core/ListItemText";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 300
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
const names = [
{ id: "1", value: "Oliver Hansen" },
{ id: "2", value: "Van Henry" },
{ id: "3", value: "Van Henry" }
];
export default function NativeSelects() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a the stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<div>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-native-simple">
Names here to select from
</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map(obj=> names[obj - 1].value).join(", ")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name.id}>
<Checkbox checked={personName.indexOf(name.id) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
Updated Code
just you can do this:
renderValue={
(selected) =>
names.filter( name => selected.includes(name.id) )
.map( record => record.name )
.join(", ")
}
To show the selected user's names, you can update the map part, where currently, you're using id.
You can update this to use the name.value to store/show the person's names.
{names.map((name) => (
<MenuItem key={name.value} value={name.value}>
<Checkbox checked={personName.indexOf(name.value) > -1} />
<ListItemText primary={name.value} />
</MenuItem>
))}
Updated Sandbox
the simple steps I did, just focus on the renderValue property, on the Select component:
renderValue={(selected) => names.find((val) => val.id === selected).value}
the logic I use, find the value in the 'names' array, where the id is the selected id, then take the 'value' value in the 'names' array, to display.
Update the Menu Items' value to be a object instead of an id.
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
name="first"
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map((item) => item.value)?.join(",")}
>
{names.map((name) => (
<MenuItem key={name.id} value={name}>
<Checkbox
checked={personName.find((p) => p.id === name.id) !== undefined}
/>
<ListItemText primary={name.value} />
</MenuItem>
))}
</Select>

How do I pass an array modifier as a prop?

I have a Select that I'm using from Ant Design used with react-final-form. So far it looks like this:
const SelectInput = (props) => (
<AntForm.Item label={props.label}>
<Select {...props.input}>
{props.options.map((option) => (
<Select.Option key={option.id} value={option.id}>
{option.name}
</Select.Option>
))}
</Select>
</AntForm.Item>
);
// ....
<AntForm layout="vertical">
<Field
label="Select an option"
name="option"
options={options}
component={SelectInput}
/>
</AntForm>
The data looks like this from the server:
const data = [
{id: 1, label: 'option1'},
{id: 2, label: 'option2'},
{id: 3, label: 'option3'},
{id: 4, label: 'option4'},
]
However as you can you the data has label in it. in my select, I'm rendering name.
I want to be able to pass a prop that modifies the array to use label. I've seen it used in some select components in other libraries, but I interested in how this is done.
i want to do something like this:
<AntForm layout="vertical">
<Field
label="Select an option"
name="option"
label={options => option.label} // <-- modify to use label here
options={data}
component={SelectInput}
/>
</AntForm>
How do I achieve something like this? If there's a way to do this with Ant Design, I would love to know as well.
Just pass a new props nameKey
const SelectInput = (props) => (
<AntForm.Item label={props.label}>
<Select {...props.input}>
{props.options.map((option) => (
<Select.Option key={option.id} value={option.id}>
{option[props.nameKey || "name"]}
</Select.Option>
))}
</Select>
</AntForm.Item>
);
<Field
label="Select an option"
name="option"
nameKey="label"
options={data}
component={SelectInput}
/>

Redux Form using Material Ui with nested MenuItem not working

I am new to React, Redux Form and Material. I would like to create a nested drop down selector component that can be dropped in a Redux Form similar to this:
Here is the renderSelectField used to create the select component.
const renderSelectField = ({
input,
label,
meta: { touched, error },
children,
...custom
}) => (
<SelectField
floatingLabelText={label}
errorText={touched && error}
{...input}
onChange={(event, index, value) => input.onChange(value)}
children={children}
{...custom}
/>
);
const MaterialUiForm = (props) => {
const { handleSubmit, pristine, reset, submitting, classes } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<Field
name="favoriteColor"
component={renderSelectField}
label="Favorite Color"
>
<MenuItem value="ff0000" primaryText="Red" />
<MenuItem value="00ff00" primaryText="Green" />
<MenuItem value="0000ff" primaryText="Blue" />
</Field>
</div>
<div>
<Field
id='chapter'
name='chapter'
component={SelectMenu}
label='Chapter'
/>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>
Submit
</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
);
};
My SelectMenu component is
const chapterFormValues = [
{
key: "international-4",
caption: "France"
},
{
key: "international-5",
caption: "Africa"
},
{
key: "international-6",
caption: "United Kingdom"
},
{
key: "usa-key",
caption: "North America",
subMenuItems: [
{
key: "usaChapter-1",
caption: "Central"
},
{
key: "usaChapter-2",
caption: "East"
}
]
}
];
const SelectMenu = (props) => {
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen((open) => !open);
};
const handleSubMenuClose = () => {
setOpen((open) => !open);
};
const { label, classes } = props;
const renderMenuItems = () => {
return (
chapterFormValues !== undefined &&
chapterFormValues.map((option) => {
if (option.hasOwnProperty("subMenuItems")) {
return (
<React.Fragment>
<MenuItem onClick={handleClick} className={classes.menuItem}>
{option.caption}
{open ? <IconExpandLess /> : <IconExpandMore />}
</MenuItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<hr />
{option.subMenuItems.map((item) => (
<MenuItem
key={item.key}
className={classes.subMenuItem}
onClick={handleSubMenuClose}
>
{item.caption}
</MenuItem>
))}
</Collapse>
</React.Fragment>
);
}
return (
<MenuItem
className={classes.menuItem}
key={option.key}
value={option.caption === "None" ? "" : option.caption}
>
{option.caption}
</MenuItem>
);
})
);
};
return (
<FormControl>
<InputLabel>{label}</InputLabel>
<MuiSelect
input={<Input id={`${props.id}-select`} />}
value={props.value}
{...props.input}
{...props.custom}
>
{renderMenuItems()}
</MuiSelect>
</FormControl>
);
};
Here is a link to the code sandbox I created. Material UI ReduxForm Select
It works except the nested drop down does not update the selector field. I have researched this and found this issue Stackoverflow redux form with nested lists but no solution.
Can anyone give me advice as to what I am missing? I believe I need to pass the event in the handleSubMenuClose function back to the Redux Form somehow but am stumped as to how to do this.
Well using Material UI MenuItem didn't work but I was able to use redux form and create a nested drop down that did.
This is a screen shot of what I created. It did not have the functionality to open/close a panel but it still gave the user a sense of a nested dropdown.
Here is the code that I changed in the SelectMenu method. The key was to use the native form of the Material UI Select component and the optgroup element.
const SelectMenu = (props) => {
const { label, classes } = props;
const renderMenuItems = () => {
return (
chapterFormValues !== undefined &&
chapterFormValues.map((option) => {
if (option.hasOwnProperty("subMenuItems")) {
return (
<React.Fragment>
<optgroup label={option.caption} className={classes.menuItem}>
{option.subMenuItems.map((item) => (
<option
key={item.key}
className={classes.subMenuItem}
>
{item.caption}
</option>
))}
</optgroup>
</React.Fragment>
);
}
return (
<option
className={classes.menuItem}
key={option.key}
value={option.caption === "None" ? "" : option.caption}
>
{option.caption === "None" ? "" : option.caption}
</option>
);
})
);
};
return (
<FormControl>
<InputLabel>{label}</InputLabel>
<Select
native
input={<Input id={`${props.id}-select`} />}
value={props.value}
{...props.input}
{...props.custom}
>
{renderMenuItems()}
</Select>
</FormControl>
);
};
Helpfully links were :
HTML / CSS: Nested <options> in a <select> field?
Redux Form Material UI: Select with Nested Lists not working

Getting Warning: Each child in a list should have a unique "key" prop. when spreading props and composing multiple Mui components

I am a bit puzzled by this, i'm composing multiple Material ui components together to mimic the pattern in TextField, for a Select Field.
When i create a story in storybook, this is fine. but when it's a server rendered page, it behaves properly but I am getting the unique key warning
export const SelectField: React.FunctionComponent<SelectFieldProps> = ({
label,
SelectProps,
children,
disabled,
helperText,
error,
className,
name,
id,
value,
onChange,
onBlur,
fullWidth,
}) => {
const labelClasses = useLabelStyles();
const helperTextClasses = useHelperStyles();
const formControlClasses = useFormControlStyles({ fullWidth });
// TODO investivate className server client mismatch with props in dev mode
const width = fullWidth ? '100%' : 'auto';
const inputLabel = React.useRef<HTMLLabelElement>(null);
const [labelWidth, setLabelWidth] = React.useState(0);
React.useEffect(() => {
setLabelWidth(inputLabel?.current?.offsetWidth ?? 0);
}, []);
return (
<FormControl
classes={formControlClasses}
style={{ width }}
className={className}
variant="filled"
error={error}
disabled={disabled}
>
{label && (
<InputLabel classes={labelClasses} htmlFor={id ? id : SelectProps?.inputProps?.id}>
{label}
</InputLabel>
)}
<Select
{...SelectProps}
labelWidth={labelWidth}
label={label}
disabled={disabled ?? SelectProps?.disabled}
name={name ?? SelectProps?.inputProps?.name}
id={id ?? SelectProps?.inputProps?.id}
value={value ?? SelectProps?.value}
onChange={onChange ?? SelectProps?.onChange}
onBlur={onBlur ?? SelectProps?.onBlur}
>
{children}
</Select>
{helperText && <FormHelperText classes={helperTextClasses}>{helperText}</FormHelperText>}
</FormControl>
);
};
and my usage of it roughly:
<Grid item xs={12} sm={sm}>
<SelectField
fullWidth
name="isoCountryCode"
id="isoCountryCode"
label="Country"
SelectProps={{
inputProps: {
'data-testid': 'country-code',
autoComplete: 'country',
},
}}
>
<option value="" />
{IsoCountryCodes.map(({ value, key, label }) => (
<option value={value} key={key}>
{label}
</option>
))}
</SelectField>
</Grid>
and yes the option values are unique. I also got the error when I took that part out :)
any ideas?
here's the issue link that was closed by the support bot:
https://github.com/mui-org/material-ui/issues/20104

Resources