Trigger api call on setting the default value of MUI Select - javascript

In Short : Execute the onChange event when default value is set in Material-UI Select Component.
When you will run this react component, you will find a Material UI Select Component with a defaut selected option as 10 which is being set using useState hook.How to make an API call at the same time when the default value is being set.
The onChange props will only execute when we change the dropdown menu item.If we are loading our page with some Select Component and setting some default menu item, then how can we make an API call and get some Data.
Use the CodeSand Box link. The alert message does not executes even though the menu-item/ option in the Select is getting changed from empty string to 10.
Assume that the default value is coming from some API call.
export default function SelectVariants() {
const [age, setAge] = React.useState("10");
//This should execute by default or on Page Load
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value);
alert("Run on load"); // Can be an API Call on the Page load And also on Subsequent onChange Events
};
return (
<div>
<FormControl variant="standard" sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-standard-label">Age</InputLabel>
<Select
labelId="demo-simple-select-standard-label"
id="demo-simple-select-standard"
value={age}
onChange={handleChange}
label="Age"
>
<MenuItem value={""}>None</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
);
}
Code Pen Example

Following this requirement:
Set a default value on the Select which is coming from an API call
(API-1) -> then using the same default value, trigger another API call
(API-2) -> then at every onChange of Select menu, also make API call
(API-2) only.
const [age, setAge] = React.useState("");
useEffect(() => {
api1().then((defaultValue) => {
setAge(defaultValue);
api2(defaultValue);
});
}, []);
const handleChange = (event) => {
setAge(event.target.value);
api2(event.target.value);
};
Working example
Hope this helps.

Related

React isn't updating a variable from a function

I'm using axios to return a map to my main app where it will be distributed to other values in the program. I am having an issue though. When I use 'onClick' on a drop down select, I want it to call that external function to return the JSON string and save it to a variable but it won't do it. I have console logged it and it says my variable is use undefined. Here is my axios code
import axios from "axios";
// ** when you launch server. Make sure 'express stuff' server is working and that it is posting to 5000/loadCycle
function Parent() {
let data = null;
console.log("called");
const url = "http://localhost:5000/";
axios
.get(`${url}loadCycle`)
.then((response) => {
data = response.data.workflow;
data = JSON.stringify(data);
data = JSON.parse(data);
//console.log(data);
const map = new Map(Object.entries(data));
console.log(map);
return map;
})
.catch((error) => console.error(`Error: ${error}`));
}
export default Parent;
and here is the code I want to format
function App() {
let dataCollection = null;
return (
<div>
<Box
sx={{ display: "flex", width: "40%", justifyContent: "space-between" }}
>
<Box sx={{ display: "flex" }}>
{/* <Typography sx={{ paddingTop: "6%" }}>Cycle</Typography> */}
{/* Cycle dropdown menu */}
{/* // MAKE CHANGES ON BRANCH // */}
<FormControl
sx={{ m: 1, minWidth: 200 }}
size="small"
variant="standard"
>
<InputLabel>Cycles</InputLabel>
<Select>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10} onClick={dataCollection=Parent()}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
{/* cycle dropdown menu end */}
</Box>
</div>
)
Why won't selecting 'one' from my select update dataCollection from 'null' to the map I am trying to return to it. Console logging it shows that the 'map' data in Parent is correct but the log for dataCollection is 'undefined'
<MenuItem value={10} onClick={dataCollection=Parent()}>Ten</MenuItem>
First of all you didn't define function (you are tried to do it like in a vainilla js, but react don't work in this way)
So, let's define separate function:
const handleSave = () => {
dataCollection=Parent()
}
// ...
<MenuItem value={10} onClick={handleSave}>Ten</MenuItem>
Next problem that's what your Parent function isn't synchronous, you should return your axios promise and after that use this function like that:
Parent().then(data => {
dataCollection = data;
})
That's not all, we can't save data at dataCollection, because your functional component this is like render function and you will lose your data on next render, so you shoud save your data to ref or state (depending on the purpose of use), let's use state:
const [dataCollection, setDataCollection] = React.useState();
// ...
const handleSave = () => {
Parent().then(data => {
setDataCollection(data);
})
}
Besides this I can see some style issues. And looks like you haven't read react doc attentively, please read againg "state and props" and "lifecycle" subjects from docs.
You have a couple of issues with your approach. I'm not sure what the other components: Box, FormControl, InputLabel, Select, and MenuItem are doing, so it makes it harder to discern if they are functioning correctly. I would simplify the code and just use regular HTML select and option tags. The select tag receives a change event and with React all events can be prefixed with "on", so it would be onChange on the select tag.
Create a prototype, a simpler project, that just focuses on that functionality until you understand it for your needs. Also, practice naming constructs a bit better, as Parent doesn't convey what it is doing. Aim to be succinct and general.

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

React: MaterialUI Select Component doesn't show passed values a prop

I have this FormControl element with Select that accepts an array of options that is being used for MenuItem options and also a value as props and this component looks like this:
const TaxonomySelector = (props) => {
const { isDisabled, taxonomies, selectedTaxonomy, handleTaxonomyChange } = props;
return (
<Grid item xs={12}>
{console.log(selectedTaxonomy)}
{console.log(taxonomies)}
<FormControl disabled={isDisabled} fullWidth>
<InputLabel>Таксономия</InputLabel>
<Select
value={selectedTaxonomy || ''}
onChange={handleTaxonomyChange}>
{Object.values(taxonomies).map((taxonomy) => (
<MenuItem key={taxonomy.id} name={taxonomy.name} value={taxonomy}>
{taxonomy.name} от {moment(taxonomy.date).format('YYYY-MM-DD')}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
);
};
The values that I pass as props are correctly displaying as filled out in the console at all stages the component is being rendered. And also in the case when this component is used for selection through using handleTaxonomyChange function everything is working correctly with user being able to select a particular option out of the array provided to the MenuItem. However, the problem occurs in case when the parent component of this component is being open for View Only or with already pre-defined values. In this case I get the following:
It seems like there's something is being passed to the Select component (even I checked through React Component in DevTools and value was showed correctly) but for some reason it is not being displayed.
The parent component contains the following code related to this part:
const INITIAL_SELECTED_TAXONOMY = null;
const [selectedTaxonomy, setSelectedTaxonomy] = useState(INITIAL_SELECTED_TAXONOMY);
const handleTaxonomyChange = (e) => setSelectedTaxonomy(e.target.value);
useEffect(() => {
getTaxonomies();
}, []);
useEffect(() => {
if (viewTypeOnlyView) {
handleStageChange(1);
handleDialogTitleChange('Конструктор КС. Режим просмотра');
}
if (viewTypeEdit) {
handleDialogTitleChange('Конструктор КС. Режим редактирования');
}
if (viewTypeCopy) {
handleDialogTitleChange('Конструктор КС. Дублирование КС');
}
if (defaultData) {
if (defaultData.name) setName(defaultData.name);
if (defaultData.taxonomy) setSelectedTaxonomy(defaultData.taxonomy);
// if (defaultData.entryPoints) setSelectedEntryPoints(defaultData.entryPoints);
if (defaultData.entryPoints) {
getEntryPointDescsFn('4.1', defaultData.entryPoints);
}
if (defaultData.message) setMessage(defaultData.message);
}
}, [viewType]);
ViewType is a prop that is being passed to this component and calling those methods in order to fill up the state with predefined values.
And the TaxonomySelector component inside the return statement:
<TaxonomySelector
taxonomies={taxonomies}
isDisabled={currentStage === 1}
selectedTaxonomy={selectedTaxonomy}
handleTaxonomyChange={handleTaxonomyChange} />
At first I thought that the issue could be related to how the component is being rendered and maybe it renders before that data pre-fill useEffect hook is being triggered. However, it seems that other elements, like the ones with string values name and message are being correctly filled out with no issues. Seems like that the issue is specifically related to Select elements. Could you let me know what could it possibly be?
Looks like disabled prop in FormControl is true.
For debug set disabled prop false

HotKey to trigger Ant Design Select to begin search

I am using 'react-hotkeys-hook' and using my hotkey works (can see log in console through onFocus()). Goal is to have a hotkey which opens and adds the cursor to the Select component. (Using ant design - 'antd')
The issue I have is when I trigger the hotkey no event is passed to the onFocus obviously - how do I pass to onFocus the event so that it works just the same as if I were to click on it? Or should I go about this a different way. I have tried using the open={true or false} inside the Select component and this works but does not add the cursor!
useHotkeys('shift+p', () => onFocus());
const hotKeyOpenSearch = () => {
// pass searchbar event here?
onFocus()
console.log('hotkey shift+p')
}
const onFocus = (e) => {
console.log(e)
}
return (
<Select
showSearch
style={{ width: searchW, paddingLeft: searchP }}
dropdownStyle={{ zIndex: 9999 }}
placeholder="🔎 Fuzzy Search"
optionFilterProp="children"
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
onSearch={onSearch}
>
</Select>
)
You are on the right track, but onFocus is for receiving events only; you cannot call that method in any way that is going to programmatically re-focus the select component. However, there is a .focus() method exposed by antd that you can call with your hotkey, but it requires getting a reference to the mounted component. Here is a working solution:
import React, { useRef } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { Select } from "antd";
export default function App() {
useHotkeys('shift+p', () => {
// kind of hackish, but without timeout,
// the hotkey will be sent to select input
setTimeout(() => {
selectRef.current.focus();
}, 20);
});
// This will hold reference to `<Select>`
const selectRef = useRef(null);
return (
<div className="App">
<Select
showSearch
style={{ width: 200, paddingLeft: 10 }}
dropdownStyle={{ zIndex: 9999 }}
placeholder="🔎 Fuzzy Search"
optionFilterProp="children"
ref={selectRef}
showAction={['focus']}
autoFocus={true}
>
<Select.Option key="blue">Blue</Select.Option>
<Select.Option key="red">Red</Select.Option>
</Select>
</div>
);
}
You'll notice that, in addition to using a ref I had to make one other significant change - changing two arguments on the <Select> component:
showAction={['focus']}
autoFocus={true}
This is because without them, it will move the cursor, but will fail to trigger the dropdown and the "fancy" select menu; see issue #8269 for details.
Sandbox Link
codesandbox.io/s/stackoverflow-65071488-t89q0
For people who want to open the dropdown on focus, use the onFocus & onBlur methods to set/unset a key in the state and use that key as the value for autoFocus.
onFocus = {() => this.setState({ isSelectFocussed: true })}
onBlur = {() => this.setState({ isSelectFocussed: false })}
showAction = "focus"
autoFocus = { this.state.isSelectFocussed }

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.

Categories