I'm receiving the data from api like cites nested into state objects
Example:
const data = [
{
id: 1,
name: 'State One',
cities: [
{
id: 1,
state: 'State One',
name: 'City One'
},
{
id: 2,
state: 'State One',
name: 'City Two'
},
{
id: 3,
state: 'State One',
name: 'City Three'
}
]
},
{
id: 2,
name: 'State 2',
cities: [
{
id: 4,
state: 'State 2',
name: 'City 5'
}
]
}
]
I have to give the Select options of the Cities according to Parent State. Lets say User selected State One in State Select Field then There should be only cities options of the State One in next City Field
How to Configure it ?
Currently; I have created hollow structure of the Select Input Fields but can't find anything how can I configure it. I need little help or any idea to start with.
This is current code;
<Col lg="6" md="12" className="mb-1">
<Label className="form-label py-1" for="state">
State
</Label>{' '}
<Row></Row>
<Select
id="state"
options={stateOptions}
defaultValue={stateOptions[0]}
onChange={(choice) => console.log(choice.value)}
/>
</Col>
<Col lg="6" md="12" className="mb-1">
<Label className="form-label py-1" for="city">
City
</Label>{' '}
<Row></Row>
<Select
id="city"
classNamePrefix="select"
options={cityOptions}
onChange={(choice) => console.log(choice.value)}
/>
</Col>
I have seen some articles but they suggest to use npm libraries but I can't use them because data is a lot different then that I want to handle.
You can keep track of the currently selected state and update the cities that the second Select takes.
Also added some comments to explain what the functions are doing.
export default function App() {
const [state, setState] = useState(null);
const [city, setCity] = useState(null);
const onStateChange = (option) => {
setState(option);
// to remove city if a different state is selected
if (!option || option?.value !== state?.value) setCity(null);
};
const onCityChange = (option) => {
setCity(option);
};
// a separate useState for cities is not needed
// as cities depend on the selected state, it can be determined when state changes
const cities = useMemo(() => {
if (!state) return [];
return getOptions(data.find((el) => el.id === state.value).cities);
}, [state]);
return (
<div>
<Select options={states} value={state} onChange={onStateChange} />
<hr />
<Select options={cities} value={city} onChange={onCityChange} />
</div>
);
}
Related
I am trying to use mui Autocomplete multi select component but can't make it work as per my requirements. I want it to have default selected values which will be passed as a props from Parent component and dropdown options which I will be getting via an API.
Problem 1 :
I am able to render my deafult values in input box which was passed as props but my selected values should not be there in options. Right now I can add duplicate values meaning even though I have few items selected by default in input box but still I could see them in dropdown. I have tried to read the docs it says The value must have reference equality with the option in order to be selected. but can't understand clearly. Is it because I am using 2 different states(arrays) for value and options?.
Problem 2 (Extra requirement)
In addition to the list of usersOptions that I will get from API to populate the dropdown option, I also need to add an option of All.
Whenever few items are already selected and then user clicks ALL in dropdown all the selected items should be removed and only All should be there in input box and when user tries to open the dropdown, all other values should be disabled.
When the user deselect ALL from the dropdown then only he/she should be able to select individual value(user)
Basically the user should be able to select only ALL or Individual user(value).
Link to CodeSanbox(https://codesandbox.io/s/naughty-elbakyan-hi16x5?file=/src/AutoComplete.js:0-1399)
import { useEffect, useState } from "react";
//This is the structure of data for both users & usersOptions
const AutoComplete = ({ users }) => {
const [selectedUsers, setSelectedUsers] = useState(users);
const [usersOptions, setUsersOptions] = useState([]);
useEffect(() => {
// fetch("https://x.com/api/getUsers")
// .then((response) => response.json())
// .then((data) => setUsersOptions(data));
//let's assume API returned this
const usersOptions = [
{ id: 1, name: "User 1" },
{ id: 2, name: "User 2" },
{ id: 3, name: "User 3" },
{ id: 4, name: "User 4" },
{ id: 5, name: "User 5" },
{ id: 6, name: "User 6" }
];
const usersOptionsWithAll = [{ id: 0, name: "All" }, ...usersOptions];
setUsersOptions(usersOptionsWithAll);
}, []);
console.log("selectedUsers", selectedUsers);
console.log("usersOptions", usersOptions);
return (
<Autocomplete
multiple
id="tags-outlined"
options={usersOptions}
getOptionLabel={(option) => option.name}
filterSelectedOptions
renderInput={(params) => (
<TextField {...params} label="" placeholder="" />
)}
value={selectedUsers}
onChange={(event, newValue) => {
setSelectedUsers(newValue);
}}
/>
);
};
export default AutoComplete;
[1]: https://i.stack.imgur.com/83V7d.jpg
1. You should use isOptionEqualToValue prop to determine if the option represents the given value.
2. It is preferable to use filterOptions prop to add 'All' option or any options you want to add.
import { Autocomplete, TextField, createFilterOptions } from "#mui/material";
import { useEffect, useState } from "react";
const AutoComplete = ({ users }) => {
const [selectedUsers, setSelectedUsers] = useState(users);
const [usersOptions, setUsersOptions] = useState([]);
useEffect(() => {
const usersOptions = [
{ id: 1, name: "User 1" },
{ id: 2, name: "User 2" },
{ id: 3, name: "User 3" },
{ id: 4, name: "User 4" },
{ id: 5, name: "User 5" },
{ id: 6, name: "User 6" }
];
setUsersOptions(usersOptions); // Set options without adds
}, []);
return (
<Autocomplete
multiple
id="tags-outlined"
options={usersOptions}
getOptionLabel={(option) => option.name}
filterSelectedOptions
renderInput={(params) => (
<TextField {...params} label="" placeholder="" />
)}
value={selectedUsers}
onChange={(event, newValue) => {
// Check if 'All' option is clicked
if (newValue.find(option => option.id === 0)) {
// Check if all options are selected
if (usersOptions.length === selectedUsers.length) {
setSelectedUsers([]);
} else {
setSelectedUsers(usersOptions);
}
} else {
setSelectedUsers(newValue);
}
}}
// Add These props
isOptionEqualToValue={(option, value) => option.id === value.id}
filterOptions={(options, params) => {
const filtered = createFilterOptions()(options, params);
return [{ id: 0, name: 'All' }, ...filtered];
}}
/>
);
};
export default AutoComplete;
Mui Autocomplete API
For example i have some working select component.
It's displays fine in Canvas tab of storybook
but in Docs tab it's doesn't display the options
// some-select-component.stories.tsx
const options = [
{
id: 1,
name: 'option 1',
},
{
id: 2,
name: 'option 2',
},
{
id: 3,
name: 'option 3',
},
];
const Template: ComponentStory<typeof SomeSelectComponent> = args => {
const [formikValue, setFormikValue] = useState({});
return (
<Formik initialValues={{} as any} onSubmit={setFormikValue} >
<SomeSelectComponent name='name'>
{options.map((option => (
<Option
key={option.id}
value={option.name}
>
{option.name}
</Option>
)))}
</SomeSelectComponent >
</Formik>
)
}
export const SimpleSelect = Template.bind({});
// docs.mdx file
<Story id='some-select-component--simple-select' />
how should i handle this case? didn't find this kind of issues
If you had strict root for portal - fix it.
difine the root block for rendering through the props, because, probably, your block for portal rendering wont be displayed in docs-root
I have a CustomInput from reactstrap that is not showing the correct state.name when clicked. I am passing a stringifiedJSON object as value on the option, then The handle change is parsing the string back into an object. I can console.log(state) and see the object just fine.
If I set the onChange to onChange={({target}) => setCountry(target.value)} this renders the selected option correctly, If I select united states, it shows united states, If I select Mexico it shows Mexico so on and so forth.
However, When I set my onChange to onChange={handleChange} with handleChange being this below, It will only show the first item in the array, In my case United States. I can select Mexico, the console.log will show the updated state, However the select option looks like United States is still chosen.
const handleChange = async(event) => {
setCountry(event.target.value)
const obj = JSON.parse(event.target.value)
setCountry(obj)
}
<Input
type="select"
id="country"
name="country"
className="mb-3"
value={country}
onChange={({target}) => setCountry(target.value)} <--- this renders correctly
>
{numberCountries.map((country, index) => (
<Fragment>
<option key={index} value={JSON.stringify(country)}>{country?.name}</option>
</Fragment>
))}
<Input
type="select"
id="country"
name="country"
className="mb-3"
value={country}
onChange={handleChange} <--- this calls update state and updates correctly but the select option wont render the correct country.name.
>
{numberCountries.map((country, index) => (
<Fragment>
<option key={index} value={JSON.stringify(country)}>{country?.name}</option>
</Fragment>
))}
</Input>
Here is the numberCountries thats being mapped over for the options
[
{
name: 'United States',
countryCode: 'US',
areaCodes: AreaCodes,
type: {
local: {
amount: '400'
},
toll_free: {
amount: '400'
},
}
},
{
name: 'Australia',
countryCode: 'AU',
type: {
local: {
amount: '1500'
},
}
},
{
name: 'Belgium',
countryCode: 'BE',
type: {
local: {
price: '410'
},
}
}
]
Instead of converting the whole country object to a string, you should use the countryCode property as its value.
Its not advised to use the index of an array as the key, so if you can use the countryCode as its key aswell.
<Input
type="select"
id="country"
name="country"
className="mb-3"
value={country.countryCode}
onChange={handleChange}
>
{numberCountries.map(({ countryCode, name }) => (
<Fragment>
<option key={countryCode} value={countryCode}>{name}</option>
</Fragment>
))}
</Input>
And then your handleChange function should be
const handleChange = async (event) => {
const obj = numberCountries.find(country => country.countryCode === event.target.value);
setCountry(obj);
};
Just remove setCountry(obj) in the handleChange
I am new to React. I'm using react-select and I've used the following code. The dropdown is displayed but I'm unable to see names and unable to view after selecting.
<Select
variant="outlined"
margin="normal"
fullWidth
value={this.state.selected}
options={RewardAutomationsList}
name="selected"
onChange={this.handleChange}
placeholder='None'
>
{RewardAutomationsList.map((option) => (
<option key={option.id} value ={option.name} label={option.name}>
{option.name}
</option>
))}
</Select>
handleChange = event => {
this.setState({
selected: event.name
});
};
The RewardAutomationsList looks like this:
RewardAutomationsList:
0:{name: "TEST 1 (INR 100)", id: "123"}
1:{name: "test 2 (INR 250)", id: "456"}
Can someone help with this?
same npm package use like this block code.
import React, { Component } from 'react'
import Select from 'react-select'
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
]
const MyComponent = () => (
<Select options={options} />
)
react-select accepts an array of objects having label and value keys. Your option objects in RewardAutomationsList have id and name keys, so it can't be displayed. You need to change them.
Also, when you subscribe to change events with react-select's onChange prop, the callback function you provide receives the selectedOption, not the event.
The following should work:
const RewardAutomationsList = [
{ label: "TEST 1 (INR 100)", value: "123" },
{ label: "test 2 (INR 250)", value: "456" },
];
class App extends React.Component {
state = {
selected: null,
}
handleChange = (selectedOption) => {
this.setState({
selected: selectedOption,
});
};
render() {
return (
<React.Fragment>
<Select
fullWidth
margin="normal"
name="selected"
onChange={this.handleChange}
options={RewardAutomationsList}
placeholder="None"
value={this.state.selected}
variant="outlined"
/>
{/* It's not necessary and it's only here to show the current state */}
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</React.Fragment>
);
}
}
I'm new to react and trying to learn on my own. I started using react-select to create a dropdown on a form and now I'm trying to pass the value of the option selected. My state looks like this.
this.state = {
part_id: "",
failure: ""
};
Then in my render
const {
part_id,
failure
} = this.state;
My form looks has 2 fields
<FormGroup>
<Label for="failure">Failure</Label>
<Input
type="text"
name="failure"
placeholder="Failure"
value={failure}
onChange={this.changeHandler}
required
/>
</FormGroup>
<FormGroup>
<Label for="part_id">Part</Label>
<Select
name="part_id"
value={part_id}
onChange={this.changeHandler}
options={option}
/>
</FormGroup>
the changeHandler looks like this
changeHandler = e => {
this.setState({ [e.target.name]: e.target.value });
};
The change handler works fine for the input but the Select throws error saying cannot read property name. I went through the API docs and came up with something like this for the Select onChange
onChange={part_id => this.setState({ part_id })}
which sets the part_id as a label, value pair. Is there a way to get just the value? and also how would I implement the same with multiselect?
The return of react-select onChange event and the value props both have the type as below
event / value:
null | {value: string, label: string} | Array<{value: string, label: string}>
So what the error means is that you can't find an attribute of null (not selected), or any attributes naming as name (you need value or label)
For multiple selections, it returns the sub-list of options.
You can find the related info in their document
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
Update
For your situation (single selection)
option having type as above
const option = [
{value: '1', label: 'name1'},
{value: '2', label: 'name2'}
]
state save selected value as id
changeHandler = e => {
this.setState({ part_id: e ? e.value : '' });
};
pick selected option item via saved id
<Select
name="part_id"
value={option.find(item => item.value === part_id)}
onChange={this.changeHandler}
options={option}
/>
For multiple selections
state save as id array
changeHandler = e => {
this.setState({ part_id: e ? e.map(x => x.value) : [] });
};
pick via filter
<Select
isMulti // Add this props with value true
name="part_id"
value={option.filter(item => part_id.includes(item.value))}
onChange={this.changeHandler}
options={option}
/>
onChange function is a bit different in react-select
It passes array of selected values, you may get first one like
onChange={([selected]) => {
// React Select return object instead of value for selection
// return { value: selected };
setValue(selected)
}}
I have tried the above solutions but some of these solutions does update the state but it doesn't gets rendered on the Select value instantly.
Herewith a demo example:
this.state = {
part_id: null,
};
handleUpdate = (part_id) => {
this.setState({ part_id: part_id.value }, () =>
console.log(`Option selected:`, this.state.part_id)
);
};
const priceOptions = [
{ value: '999', label: 'Item One' },
{ value: '32.5', label: 'Item Two' },
{ value: '478', label: 'Item Three' }
]
<Select
onChange={this.handleUpdate}
value={priceOptions.find(item => item.value === part_id)}
options={priceOptions}
placeholder={<div>Select option</div>}
/>