Select Not Displaying Value (ReactJS/Material UI) - reactjs

I am trying to create a multi-select form control, however, whenever I select something it does not get rendered. The function handleChange does get the event.target.value but it does not seem to add to the roleIds state. Furthermore, the console.log for the variable selected does not log anything to console.
Component Code:
const allRoleIds = [
"12345678",
"98765423",
"56465735683578",
];
const [roleIds, setRoleIds] = React.useState([]);
function handleChange(event) {
setRoleIds(event.target.value);
}
const [cassowaries, setCassowaries] = React.useState({
columns: [
{ title: "Cassowary Name", field: "name" },
{
title: "Cassowary Roles",
field: "roles",
render: (rowData) => {
return (
<li>
{rowData.roles.map((role) => (
<Chip label={role} className={classes.chip} />
))}
</li>
);
},
editComponent: (props) => (
<FormControl className={classes.formControl}>
<InputLabel>Roles</InputLabel>
<Select
multiple
value={roleIds}
onChange={handleChange}
input={<Input id="select-multiple-chip" />}
renderValue={(selected) => {
console.log(selected);
return (
<div className={classes.chips}>
{selected.map((value) => (
<Chip
key={value}
label={value}
className={classes.chip}
/>
))}
</div>
);
}}
// MenuProps={MenuProps}
>
{allRoleIds.map((id) => (
<MenuItem key={id} value={id}>
{id}
</MenuItem>
))}
</Select>
</FormControl>
),
},
{ title: "Penguin", field: "penguin" },
],
data: [{ name: "Mehmet", roles: roleIds, penguin: true }],
});

You are doing concat in your handleChange. Material ui already gives you the array of selected values to you. Sp fix your handleChange everything should be fine.
Like this
function handleChange(event) {
setRoleIds(event.target.value);
}
Working demo is here
EDIT:
Based on additional req (see comments):
If a custom multi select component needs to be used inside materail table editcomponent, then the state of select should not be managed outside but state & onChagne needs to managed inside editComponent using the prop it provides.
See working demo here
Code snippet - material ui table
...
columns: [
{ title: "Tag Name", field: "name" },
{
title: "Tag Roles",
field: "roles",
render: rowData => {
return (
<li>
{rowData.roles.map(role => (
<Chip label={role} className={classes.chip} />
))}
</li>
);
},
editComponent: props => {
console.log("props", props);
return (
<FormControl className={classes.formControl}>
<InputLabel>Roles</InputLabel>
<Select
multiple
value={props.value}
onChange={e => props.onChange(e.target.value)}
input={<Input id="select-multiple-chip" />}
renderValue={renderChip}
>
{allRoleIds.map(id => (
<MenuItem key={id} value={id}>
{id}
</MenuItem>
))}
</Select>
</FormControl>
);
}
},
{ title: "Penguin", field: "penguin" }
],
data: [{ name: "Mehmet", roles: [], penguin: true }]
...

Related

React Multi select- how to filter through table columns

React-table multi select filter not filtering data
Hi there, just wondering if anyone has a solution for this? I am struggling with the same issue.
I have a multi select dropdown which needs to show any changes in a react table.
so these are my columns: { Header: 'First Name', accessor: 'first_name', Cell: ({ cell }) => <ColorCell cell={cell} name="first_name" />, }, { Header: 'Last Name', accessor: 'last_name', Cell: ({ cell }) => <ColorCell cell={cell} name="last_name" />, },
an example of my dropdown:
//create a state to store the value const [valueState, setValueState] = useState(''); //handler function const handleSelectChange = (value) => { setValueState(value); } // on your onChange prop <SelectBox value={valueState} defaultValue="" placeholder="Select columns" isMulti options={filterOptions} onChange={ (value) => handleSelectChange (value) } name="ColumnSelect" label="" isClearable />
this is how I check for changes in the api:
{_.includes(cell.row.values.has_changed, name) ? ( <p style={{ backgroundColor: ${getCellColor(name)} }}>{cell.value}</p> ) : ( <p style={{ backgroundColor: 'white' }}>{cell.value}</p> )}

How to use option value in React Select

I am learning react and it's fun!
I am creating a sign up form in React using react-select.
I am mapping values from array:
const options = [ 'female', 'male', 'other']
<Select {...register("gender")}
label="Gender"
fullWidth
variant="outlined"
>
{options.map(value => (
<option key={value} value={value}>
{value}
</option>
))}
</Select>
But I get following warning:
Warning: Use the defaultValue or value props on instead of setting selected on .
Questions:
How do I use the defaultValue or value props on ?
I am setting label="Gender" but it is not visible, why?
const options = [
{ value: "female", label: "Female" },
{ value: "male", label: "Male" },
{ value: "other", label: "Other" }
];
export default function App() {
const [gender, setGender] = useState("");
const handleChange = (event) => {
setGender(event.target.value);
};
return (
<div className="App">
<Select
value={gender}
onChange={handleChange}
>
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
</div>
);
}
API reference: https://material-ui.com/api/select/
Working demo: https://codesandbox.io/s/pedantic-jones-dyfwd?file=/src/App.js:119-788

Marerial Table select menu is connected to each row?

My Material-Table has a select menu as one of the columns, however when i select one of the options it changes it for all the rows. How can I make the change row specific so that they are not connected to each other? I realize that the event.target.value approach is applied to all of the mapped select menues, I cant figure out how to make it specific to one.
The row in question is the Facility column
const selectData = ["One", "Two", "Three"];
function Table() {
const columns = [
{
title: "Avatar",
render: (rowData) => (
<Avatar
maxInitials={1}
size={40}
round={true}
name={rowData === undefined ? " " : rowData.first_name}
/>
),
},
{ title: "ID", field: "_id", hidden: true },
{ title: "Name", field: "name" },
{ title: "Email", field: "email" },
{ title: "Company", field: "companyID" },
{ title: "Role", field: "role" },
{
title: "Facility",
render: (rowData) => (
<FormControl>
<InputLabel id="demo-mutiple-checkbox-label">Tags</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={items}
onChange={handleChange}
input={<Input />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{selectData.map((item) => (
<MenuItem key={item} value={item}>
<Checkbox checked={items.indexOf(item) > -1} />
<ListItemText primary={item} />
</MenuItem>
))}
</Select>
</FormControl>
),
},
{ title: "Password", field: "password" },
];
const [data, setData] = useState([]);
const [items, setItems] = useState([]); //table data
const handleChange = (event) => {
setItems(event.target.value);
};
return (
<div>
<MaterialTable
title="Users"
columns={columns}
data={data}
icons={tableIcons}
editable={{
onRowUpdate: (newData, oldData) =>
new Promise((resolve) => {
handleRowUpdate(newData, oldData, resolve);
}),
onRowAdd: (newData) =>
new Promise((resolve) => {
handleRowAdd(newData, resolve);
}),
onRowDelete: (oldData) =>
new Promise((resolve) => {
handleRowDelete(oldData, resolve);
}),
}}
/>
</div>
);
}
I think you should define an editComponent in the Facility column, by doing so you will able to modify the value of the current rowData and not just the items variable which is being set as valuein the select component and therefore shown in every row.
Here is an example:
const tableColumns = [
{ title: "Client", field: "id" },
{ title: "Name", field: "name" },
{
title: "Choose a Fruit",
field: "fruit",
editComponent: ({ value, onChange }) => (
<select onChange={(e) => onChange(e.target.value)}>
<option selected value={value}>
{value}
</option>
{fruitsList.map(
(item) =>
item !== value && (
<option key={item} value={item}>
{item}
</option>
)
)}
</select>
)
}
];
Hope that works for you! Full code and sandbox here.

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

Formik arrayHelpers remove is not removing the selected item

I am using Formik's arrayHelpers to push and remove objects from an array of objects. The push works as expected and adds the object and its values to the array. The remove works when you only select one item in the array and then deselect it. The problem, however starts when you select multiple items. For instance, you select the first item and it is added to the array and then select the second item and it is added to the array, but say you want to deselect that second item and you click on it, the first item is deselected. Then if you click on it again, it deselects the second item.
initialValue: lenders: []
/*mock array of lenders*/
const lenders = [
{
id: 1,
text: 'Lender1',
value: 'Lender1',
},
{
id: 2,
text: 'Lender2',
value: 'Lender2',
},
{
id: 3,
text: 'Lender3',
value: 'Lender3',
},
]
<FieldArray
name="lenders"
render={arrayHelpers => (
<div className="lenders">
{lenders.map(lender => (
<div key={lender.id}>
<Field name={`lender${lender.id}`}>
{({ field, meta }) => (
<SelectableCard
id={field.name}
name={field.name}
text={lender.value}
isSelected={props.values.lenders.some(
len => len.id === lender.id
)}
label={lender.value}
inputName={field.name}
value={lender.value}
ref={field.ref}
onClick={evt => {
let isSelected = props.values.lenders.some(
len => len.id === lender.id
);
isSelected
? arrayHelpers.remove(lender)
: arrayHelpers.push({
id: lender.id,
value: lender.value,
});
field.onChange({
target: {
id: lender.id,
value: !isSelected,
},
});
}}
/>
)}
</Field>
</div>
))}
</div>
)}
This was solved by changing the isSelected const in the onClick to finding the index like this:
const index = props.values.lenders.findIndex(
len => len.id === lender.id
);
Then wrapping the arrayHelpers to check if the index is greater than -1:
index > -1
? arrayHelpers.remove(index)
: arrayHelpers.push({
id: lender.id,
value: lender.value,
});
FieldArray's arrayHelpers render props takes an index of the item to be removed and not the object to be removed
Pass on the index that you obtain from map
<FieldArray
name="lenders"
render={arrayHelpers => (
<div className="lenders">
{lenders.map((lender, index) => (
<div key={lender.id}>
<Field name={`lender${lender.id}`}>
{({ field, meta }) => (
<SelectableCard
id={field.name}
name={field.name}
text={lender.value}
isSelected={props.values.lenders.some(
len => len.id === lender.id
)}
label={lender.value}
inputName={field.name}
value={lender.value}
ref={field.ref}
onClick={evt => {
let isSelected = props.values.lenders.some(
len => len.id === lender.id
);
isSelected
? arrayHelpers.remove(index)
: arrayHelpers.push({
id: lender.id,
value: lender.value,
});
field.onChange({
target: {
id: lender.id,
value: !isSelected,
},
});
}}
/>
)}
</Field>
</div>
))}
</div>
)}
As mentioned above you should remove the index of the item/object not the object itself.
I encountered this while integrating Formik with ChipInput and solved as follows:
<FieldArray
name="topics"
render={arrayHelpers => (
<Field
name="topics"
component={ChipInput}
type="text"
label="Topics"
value={values.topics}
margin="none"
onAdd={chip => arrayHelpers.push(chip)}
onDelete={chip =>
arrayHelpers.remove(values.topics.indexOf(chip))
}
fullWidth
chipRenderer={chipRenderer}
helperText={errors.topics ? errors.topics : ''}
error={errors.topics ? true : false}
/>

Resources