Fix .map is not a function in a functional component - javascript

I have a Textbox where I set array items to the textbox and I use it as a drop down. My code works and I am able to see the items in the drop down, however when i click on any other item apart from the first item I get the error groups.map is not a function.
I set array from the API to my groups and then I try to map the items that I need into the textbox.
Please see my code below and assist me to fix it.
I set my state to an empty array here
const [groups, setGroups] = useState([]);
const handleChange = event => {
setGroups({
...groups,
[event.target.name]: event.target.value
});
};
I handle my API call here
axios
.post('http://', formData, config)
.then((response) => {
console.log(response.data)
setGroups(response.data);
})
.catch((error) => {
console.log(error.response);
});
}, []);
I render with my data to show in the drop down here
<TextField
fullWidth
label="Select Group"
margin="dense"
name="groups"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{ native: true }}
value={groups.name}
variant="outlined"
>
{groups.map(option => (
<option
key={option.id}
>
{option.name}
</option>
))}
</TextField>

It is because axios is not done with the request and your state has not been updated yet when you try to map over your state. Try this piece of code near the map:
{groups && groups.map(option => (
<option
key={option.id}
>
{option.name}
</option>
))}
The map will go over the array only if groups exists

Try adding a new state variable to store the selected group.
const [selected, setSelected] = useState();
Your handleChange function will become something like
const handleChange = event => {
setSelected(event.target.value);
};
And your TextField component will have it's value changed to match the corresponding state variable:
<TextField
fullWidth
label="Select Group"
margin="dense"
name="groups"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{ native: true }}
value={selected}
variant="outlined"
>
{groups.map(option => (
<option
key={option.id}
>
{option.name}
</option>
))}
</TextField>

I had same problem look for response that you are getting if it is an object you can convert it to array in api by
Object.entries(images)
Then send this response also in client side when you request data set data with res.data otherwise it will be huge object.....

Related

How can get the current state of the material UI component without clicking on any event in react functional component?

I am trying to get Material Ui component current values after page reloads or DOM change without clicking on any event. The values are showing from the database.
Basically, I am checking after clicking on the save button if the values are updated or not, if the values are previous values I will return nothing. My function is ready but I am not able to get the current values when the page reloads, I am getting status state null.
My UI, the data is showing from the database:
Select component:
const [status, setStatus] = useState<string | null>(null); // My state
data.map(details=> {
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-controlled-open-select-label">Status</InputLabel>
<Select
labelId="demo-controlled-open-select-label"
id="demo-controlled-open-select"
open={open}
onClose={handleClose}
onOpen={handleOpen}
value={details.status || status} // value from DB
label="Status"
onChange={(e) => setStatus(details.status || e.target.value)}
>
<MenuItem value={'Placed'}>
<em>Placed</em>
</MenuItem>
<MenuItem value={'Packed'}>Packed</MenuItem>
<MenuItem value={'Shipped'}>Shipped</MenuItem>
<MenuItem value={'Delivered'}>Delivered</MenuItem>
<MenuItem value={'Cancel'}>Cancel</MenuItem>
</Select>
</FormControl>
<button onClick={()=>validate (details)}>save</button>
})
My function:
const validate = (details)=>{
if (details.status !== status) {
console.log('Do somthing')
} else {
console.log('None')
}
}
why did you give initial value null to state , try changing that initial value to what you want

How do I get value of mapped options onChange of select dropdown in react?

I have a list of option, I'm getting from the API. I'm using .map() and When I change the option I want the specific option object so I can store that inside of redux store.
<select
onChange={() => handleChange()}
className="pl-2 bg-white font-medium border-none"
>
{outlets.map((outletItem) => (
<option key={outletItem.outlet_id} value={outletItem.outlet_id}>
{outletItem.name}
</option>
))}
</select>
Here, outlets is an array from the API. I want the specific value of the array when I make a change.
handleChange()
const handleChange = () => {
dispatch(addSelectedOutlet(outlets));
};
You need to catch the value of onChange
onChange={(e) => handleChange(e)}
then
const handleChange = (e) => {
console.log(e.target.value)
};

React First Item in a TextField Select does not get Selected

I have json payload that is passed as a key value pair. I need to populate the data in a TextField select which acts as a drop down. I am able to select the first or second options in the drop down if there is two or more items in the drop down. However, when there is a single item in the drop down it does not get selected even when I click on it.
This is my code below where I set my state as I am using a functional component:
const [departments, setDepartments] = useState([]);
const [selected, setSelected] = useState();
This is code below that checks if the item in the TextField is clicked:
const handleChange = event => {
setSelected(event.currentTarget.id);
};
Also this is my code below that I set the TextField with the data that I receive from the API:
<TextField
fullWidth
label="Select Department"
margin="dense"
name="departments"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{ native: true }}
value={selected}
variant="outlined"
>
{departments.map(option => (
<option
key={option.id}
value={option.id}
>
{option.department}
</option>
))}
</TextField>
Kindly help me resolve this. So I can get to set the first item in the drop down even if it is the only item in the drop down.
you had some error on your code like you used event.currentTarget.id but you should use event.target.value.
However, when there is a single item in the drop down it is not called handleChange when you click on it, because handleChange is onChange event. If you have one item, you cannot change item as there is only one, so onChange event is not fired. Instead, you can add another item like "Please select" then you can select your single item. Please check this example:
import React, {useEffect, useState} from "react";
import TextField from "#material-ui/core/TextField";
function TextFieldDDL() {
const [selected, setSelected] = useState();
const departments=[
{id: -1, department: 'Please Select...'},
{id: 1, department: 'CSE'},
// {id: 2, department: 'BBA'},
// {id: 3, department: 'EEE'}
];
function handleChange(e){
console.log(e.target.value, 'e');
setSelected(e.target.value);
}
return (
<React.Fragment>
<TextField
fullWidth
label="Select Department"
margin="dense"
name="departments"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{native: true}}
value={selected}
variant="outlined"
>
{departments.map(option => (
<option
key={option.id}
value={option.id}
>
{option.department}
</option>
))}
</TextField>
</React.Fragment>
);
}
export default TextFieldDDL;
Update: Add item into the list
const departments = [{id: -1, department: 'Please Select...'}, ...response.data];
setDepartments(departments);

MaterialUI Select set value is always out of range

i've a MaterialUI Select code, and i'm handling the value parameter dynamically. My problem is, when i set any value, it says always it's out of range, even showing the value in the valid values.
SelectInput.js:291 Material-UI: you have provided an out-of-range value `100001,250000` for the select (name="followers") component.
Consider providing a value that matches one of the available options or ''.
The available values are `0,50000`, `50001,100000`, `100001,250000`, `250001,500000`, `500001,750000`, `750001,9007199254740991`.
(anonymous) # SelectInput.js:291
And this is my code simplified:
const followers = [
{ '0-50k': [0, 50000] },
{ '50k-100k': [50001, 100000] },
{ '100k-250k': [100001, 250000] },
{ '250k-500k': [250001, 500000] },
{ '500k-750k': [500001, 750000] },
{ '+1M': [750001, Number.MAX_SAFE_INTEGER] },
];
<div className={classes.formGroup}>
<InputLabel id="followersL">Followers</InputLabel>
<Select
className={classes.field}
fullWidth
id="followers"
labelId="followersL"
margin="dense"
displayEmpty
name="followers"
onChange={(event) => setValue(event.target.value)} //I've updated the sate with the new value
value={
filters.basicInfo.followers
? value
: ''
}
variant="outlined"
>
{followers.map((element) => (
<MenuItem
value={element[Object.keys(element)]}
key={Object.keys(element)[0]}
>
{Object.keys(element)[0]}
</MenuItem>
))}
</Select>
</div>
As you can see in the message, the value selected 100001,250000 it's inside the range examples 100001,250000
Where is the problem?
add this defaultValue = "" like this
<Select
...
defaultValue=""
>
This advice may be useful for others:
If the value for Select element is object, it should be the exact instance of the object from the list of Options.
For example:
const [test, setTest] = useState("");
//list of options for Material UI select
const testOptions = [
{name: "123"},
{name: "456"},
{name: "769"},
];
//let's try to change value to {name: "123"} using JS
setTest(testOptions[0]); // everything is OK
setTest({name: "123"}); // Error! You provided out of range value!
Stringifying your value will get this to work.
element[Object.keys(element)] + ""}
If you needed it to be in its original array format before sending the result to your server you could use a function like this to do this
const strToArr = str => str.split(',').map(item => Number(item))
In my code here I have used your provided example and been able to replicate your error. But Stringifying the value removes the error and gets it to work as expected.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import Select from "#material-ui/core/Select";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
export default function SimpleSelect() {
const classes = useStyles();
const followers = [
{ "0-50k": [0, 50000] },
{ "50k-100k": [50001, 100000] },
{ "100k-250k": [100001, 250000] },
{ "250k-500k": [250001, 500000] },
{ "500k-750k": [500001, 750000] },
{ "+1M": [750001, Number.MAX_SAFE_INTEGER] }
];
const [value, setValue] = React.useState("");
const handleChange = event => setValue(event.target.value);
return (
<div>
<p>value - {value}</p>
<div className={classes.formGroup}>
<InputLabel id="followersL">Followers</InputLabel>
<Select
className={classes.field}
fullWidth
id="followers"
labelId="followersL"
margin="dense"
displayEmpty
name="followers"
onChange={handleChange}
value={value}
variant="outlined"
>
{followers.map(element => (
<MenuItem
value={element[Object.keys(element)] + ""}
key={Object.keys(element)[0]}
>
{Object.keys(element)[0]}
</MenuItem>
))}
</Select>
</div>
</div>
);
}
I ran into the same problem (you have provided an out-of-range value) when using a number state with a default value of -1:
const [selectedAccountId, setSelectedAccountId] = useState<number>(-1);
The solution to this problem was to assign an empty string for the value property in Material's UI Select component instead of using the default value of -1:
value={selectedAccountId === -1 ? '' : selectedAccountId}
Full component example:
<FormControl fullWidth>
<InputLabel>Account</InputLabel>
<Select
id="account"
value={selectedAccountId === -1 ? '' : selectedAccountId}
onChange={event => {
setSelectedAccountId(Number(event.target.value));
}}>
{allAccounts.map((account, index) => (
<MenuItem key={index} value={account.id}>
{account.exchange} ({account.id})
</MenuItem>
))}
</Select>
</FormControl>
From some research, what I've come to understand to be the reason for this warning, in my case, is the MUI Select was trying to map over a list of options that were not available on the first render as the list was coming from Axios response.
I made a component named Dropdown that renders the MUI Select component. I was providing it four props:
options: the list of options,
initialValue: the default value as I
had different default values for different instances of the Dropdown component that were not the first item of the
options list always
... and 2 other props that are not scoped for this discussion.
So, for each instance of the Dropdown component, I had to check whether the options list has any data, and only then render it. And this is what removed the warning from the console. To give a glimpse of what I did:
{viewsData.length > 0 && (
<Dropdown
options={viewsData}
initialValue={7}
{...otherProps}
/>
)}
This was bugging me for a long time. Hopefully this will help someone.
I got the same error and I solved it by making sure that the default value and the other select values thereafter are the same, for example if the default value is a string like '' then the other values are objects it will show the warning so to avoid such a problem make the default value to be either a [] or {} or most preferably null
To add to #Ait Friha Zaid response.
I also added the defaultValue attribute but also added an additional option:
const values = ['option 1', 'option 2', 'option 3'];
<FormControl fullWidth>
<InputLabel>{title}</InputLabel>
<Select
defaultValue="choose"
label={title}
onChange={e => func({...state, [keyName]: e.target.value}) }
>
<MenuItem disabled value="choose">Choose Option</MenuItem>
{values.map((value) => (
<MenuItem value={value} key={value}>{value}</MenuItem>
))}
</Select>
</FormControl>
That way you always have a disabled option that works as a placeholder which is the default option, and in case you want to do form validation, until the user changes the option, the state wont be changed.

TextField values are equal to "undefined" in ReactJS

I am very new to ReactJs and Material-UI. Therefore I apologize if my question is dumb.
So, I have a file Main.js that contains the following lines of code:
handleChange = (name, event) => {
if(event==null)
return;
console.log(event)
this.setState({
[name]: event.value
}, () => {
console.log("my_num_var",this.state.my_num_var)
console.log("my_combobox_var",this.state.my_combobox_var)
});
};
Then, I have TopControls.js with the following code:
<Grid item xs={true}>
<TextField
name="my_num_var"
id="my_num_var"
label="my_num_var"
onChange={(event) => this.props.handleChange("my_num_var", event)}
value={this.props.state.my_num_var}
type="number"
className={this.props.styles.textField}
margin="normal"
InputProps={{
startAdornment: <InputAdornment position="start">(seconds)</InputAdornment>,
}}
/>
</Grid>
Each time when I change the value of my_num_var (TextField), I see undefined in console (console.log("my_num_var",this.state.my_num_var)).
In the handleChange function, the console.log(event) outputs:
SyntheticEvent {dispatchConfig: {…}, _targetInst: FiberNode,
nativeEvent: InputEvent, type: "change", target:
input#visibility.MuiInputBase-input-269.MuiInput-input-254.MuiInputBase-inputType-272.MuiInput-inpu…,
…}
So, basically event.value returns nothing. The value is set only if I do event.target.value. But in this case, the comboboxes do not work because they need event.value. How can I distinguish between the evnts?
The comboboxes work fine with event.value and I can see correct values of my_combobox_var in console.
<Grid item xs={true}>
<FormControl
className={this.props.styles.formControl}
margin="normal">
<InputLabel shrink htmlFor="my_combobox_var-label-placeholder">
my_combobox_var
</InputLabel>
<Select
onChange={(event) => this.props.handleChange("my_combobox_var", event)}
className="basic-single"
classNamePrefix="select"
defaultValue={this.props.state.my_combobox_var}
isClearable={this.state.isClearable}
isSearchable={this.state.isSearchable}
name="my_combobox_var"
options={this.t}
styles={myStyle}/>
</FormControl>
</Grid>
I am not sure if the code that I posted above is enough to help. If I should add more details, please let me know. I would appreciate any clues. Thanks.
Instead of passing the whole event to handleChange you could pass only the value to make it simpler.
Since the text input needs event.target.value instead of event.value this will allow you to use the same handler for multiple event types.
onChange={(event) => this.props.handleChange("my_num_var", event.target.value)
onChange={(event) => this.props.handleChange("my_num_var", event.value)
Then just rewrite your handler to expect the value instead of the event:
handleChange = (name, value) => {
if(value==null)
return;
this.setState({
[name]: value
},
...

Categories