Process the prop from another component to match in the react-select options
const labelOptionsProcessed = []
labelOptions.map(item => {
let tmpObj = {
id: item.id,
label: item.name,
name: item.name
}
labelOptionsProcessed.push(tmpObj)
})
tmpObj is the structure of the options
<Select
options={labelOptionsProcessed}
isMulti
></Select>
When a select a dropdown value , the options change to no data available
Before selecting an Option :
After Selecting an Option:
StackBlitz: https://stackblitz.com/edit/react-4ddnq7
You have to set a value property for options instead of id
EX:
const labelOptionsProcessed = []
labelOptions.map(item => {
let tmpObj = {
value: item.id, // here
label: item.name,
name: item.name
}
labelOptionsProcessed.push(tmpObj)
});
I guess this is not the proper way to generate the options list, I'd do something like this
const labelOptionsProcessed = labelOptions.map(({ value, name: label, name }) => {
return {
value,
label,
name
};
});
Related
I load my supplier using SWR. But it is wrapped in various classes. I get it like so:
const supplier = new Supplier(supplierID)
This gives me a supplier object.
I then have a select drop down:
options = assetPriceCategories.map((category) => {
return {
value: category.node.id,
label: category.node.name,
property: 'assetPriceCategory'
}
})
selected = { value: supplier.assetPriceCategory.id, label: supplier.assetPriceCategory.name }
<Select name="assetPriceCategory"
instanceId={useId()}
value={selected}
options={options}
onChange={option => supplierDataChange(option)}
/>
This works, the select shows and the options show, and the selected item is selected. My onChange:
const supplierDataChange = (object) => {
if(typeof supplier[object.property] === 'object') {
supplier[object.property].id = object.value
} else {
supplier[object.property] = object.value
}
}
Doing console.log() at the end of this function proves that the supplier has changed, but the <Select> object does not update, it remains the same option as loaded in the beginning.
I am using React semantic ui. I am rendering a dropdown in Fieldset. I have written code such that, once a option is selected, the options is updated such that the selected option is removed from the list. But when I select an option from the dropdown, the selected value is not displayed, rather it shows empty.
Here is my code:
This is my dropdown code:
<Dropdown
name={`rows.${index}.mainField`}
className={"dropdown fieldDropdown"}
widths={2}
placeholder="Field"
fluid
selection
options={mainFieldOptions}
value={row.mainField}
onChange={(e, { value }) => {
setFieldValue(`rows.${index}.mainField`, value)
updateDropDownOptions(value)
}
}
/>
My options:
let mainField = [
{ key: "org", text: "org", value: "org" },
{ key: "role", text: "role", value: "role" },
{ key: "emailId", text: "emailId", value: "emailId" },
]
Also, I have:
const [mainFieldOptions, setMainFieldOptions] = useState(mainField)
And,
const updateDropDownOptions = (value:any) => {
let updatedOptions: { key: string; text: string; value: string }[] = []
mainFieldOptions.forEach(option => {
if(option.key != value){
updatedOptions.push({ key:option.key , text:option.key, value:option.key })
}
})
setMainFieldOptions(updatedOptions)
console.log("mainfield", mainField)
}
In onChange, if I dont call updateDropDownOptions() method, the dropdown value is set. But when I call the method, its giving blank value. Please help.
There are few changes required in your code,
You are pushing the entire initialValues when you are adding a row which is an [{}] but you need to push only {} so change your code to initialValues[0] in your push method.
Its not needed to maintain a additional state for the options. You can filter the options based on the selected option in other rows which is available in the values.rows .
Util for filtering the options
const getMainFieldOptions = (rows, index) => {
const selectedOptions = rows.filter((row, rowIndex) => rowIndex !== index);
const filteredOptions = mainField.filter(mainFieldOption => !selectedOptions.find(selectedOption => mainFieldOption.value === selectedOption.mainField));
return filteredOptions;
}
Call this util when rendering each row
values.rows.length > 0 &&
values.rows.map((row, index) => {
const mainFieldOptions = getMainFieldOptions(values.rows, index);
Working Sandbox
I am working with react and I have a dropdown menu which is generated like that:
Select a user : <select defaultValue={'DEFAULT'} onChange={this.handleFilter}><option value="DEFAULT" disabled>-- select an gangster --</option>{ddUsers}</select>
ddUsers:
let ddUsers = this.state.users.map(user => <option key={uuid.v4()} value={user}>{user.name}</option>)
And here is my users array:
users : [
{ name: 'All', key : uuid.v4()},
{ name: 'Koko', key : uuid.v4()},
{ name: 'Krikri', key : uuid.v4()},
{ name: 'Kéké', key : uuid.v4()}
]
My problem is that when I want to use the event.target it return me the entire select tag (here the output of console.log(event.target):
<select>
<option value="DEFAULT" disabled selected>-- select an gangster --</option>
<option value="[object Object]">All</option>
<option value="[object Object]">Koko</option>
<option value="[object Object]">Krikri</option>
<option value="[object Object]">Kéké</option>
</select>
where it should normally return me only the user.
here is my handleUser (which displays me select tag above):
handleFilter = (event) => {
console.log(event.target);
}
I am lost on what I am missing. I have something similar and it's working perfectly.
What you want is a key/value at select value, but unfortunately, it does not work.
Maybe you'll need to use another component like React-select or use JSON.stringfy() and JSON.parse()
I have made an abstraction code using JSON methods.
const uuid = {
v4() {
return Math.random();
}
};
const users = [
{ name: "All", key: uuid.v4() },
{ name: "Koko", key: uuid.v4() },
{ name: "Krikri", key: uuid.v4() },
{ name: "Kéké", key: uuid.v4() }
];
function App() {
const [selected, setSelected] = React.useState("");
function parseSelected(event) {
const valueToParse = event.target.value;
const itemSelected = JSON.parse(valueToParse);
setSelected(itemSelected);
return;
}
return (
<div>
<select name="any" id="any" onChange={parseSelected}>
{users.map(user => (
<option key={user.key} value={JSON.stringify(user)}>
{user.name}
</option>
))}
</select>
<p>Selected name: {selected.name}</p>
<p>Selected key: {selected.key}</p>
</div>
);
}
the reason is Option HTML interface accepts only DOMString as a value.
You can see the docs here
You could do something like this.. This allows for flexibility so you can use this <select> component anywhere, using any object with any property.. (using #CarlosQuerioz answer, you'd always have to use an object with a name key)
Parameter meanings:
data: array you are wanting to use as options for select
value: the key from your data property that you want to display
placeholder: placeholder value (not selectable)
defaultOption: default selected option (more on this below)
onSelectChange: event that is fired when selection changes
How we handle defaultOption:
If you pass in an array of objects to defaultOption we will select the first object in that array, and use the specified value key to determine the value to be shown
If you pass in an object, we will show the specified value key
To demonstrate the defaultOption, uncomment each of these values in the example below, you'll see what I mean.
const users = [ // If this was your `data` object
{ name: "Karkar", key: 1 },
{ name: "Koko", key: 2 },
{ name: "Krikri", key: 3 },
{ name: "Kéké", key: 4 }
];
<MySelect
data={users}
value="name" // Must be a valid key that your objects have
onSelectChange={handle}
defaultOption={users[0].name}
//defaultOption={users[1]} // Would display 'Koko' by default
//defaultOption={users} // Would display 'Karkar' by default
/>
EXAMPLE:
const { useState, useEffect } = React;
const { render } = ReactDOM;
function MySelect({ data, value, defaultOption = "", onSelectChange, placeholder = "Select an option" }) {
const [selected, setSelected] = useState(defaultOption);
const handleOnSelectChange = selection => {
onSelectChange && onSelectChange(selection);
}
const handleChange = event => {
let sel = data.find(d => d[value] === event.target.value);
setSelected(sel);
handleOnSelectChange(sel);
};
useEffect(() => {
if (typeof selected === "object") {
let val = selected.length > 0 ? selected[0] : selected;
setSelected(val);
handleOnSelectChange(val);
}
}, []);
return (
<select value={selected[value]} onChange={handleChange}>
<option value="" disabled>
{placeholder}
</option>
{data && data.map((itm, indx) => (
<option key={indx} value={itm[value]}>
{itm[value]}
</option>
))}
</select>
);
}
const users = [
{ name: "Karkar", key: 1 },
{ name: "Koko", key: 2 },
{ name: "Krikri", key: 3 },
{ name: "Kéké", key: 4 }
];
function App() {
const [user, setUser] = useState();
const handle = selectedUser => {
setUser(selectedUser);
};
return (
<div>
<MySelect
data={users}
value="name"
placeholder="Please make a selection"
onSelectChange={handle}
//defaultOption={users[0].name}
//defaultOption={users[0]}
defaultOption={users}
/>
{user && <pre>{JSON.stringify(user, null, 2)}</pre>}
</div>
);
}
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
I have a config file with some fields for generating input elements inside a component.
I'm trying to generate an AsyncSelect input field and assigning it loadOptions prop through the config. Problem is that the function never gets called.
Here's the object in the configuration for generating the AsyncSelect input:
{
key: 'cityCode',
value: (customer, borrower) => ({
label: borrower.cityName,
value: borrower.cityCode
}),
text: 'city',
disabled: falseFn,
input: REACT_ASYNC_SELECT_INPUT,
props: (borrower, component) => ({
inputValue: component.state.cityName,
loadOptions: () => {
return CitiesService.find(component.state.cityName || '')
.then(records => records.map(record => ({
label: record.name,
value: record.code
})));
},
onInputChange: cityName => {
component.setState({
cityName
});
},
onChange: ({label, value}, {action}) => {
if (action === 'clear') {
component.updateCustomer(component.props.fieldKey + 'cityName', '');
component.updateCustomer(component.props.fieldKey + 'cityCode', -1);
} else {
component.updateCustomer(component.props.fieldKey + 'cityName', label);
component.updateCustomer(component.props.fieldKey + 'cityCode', value);
}
}
}),
render: trueFn
},
Here's the part of the component render utilizing the config file to render different inputs:
<div className="values">
{
BorrowerInfoConfig().map(field => {
if (field.render(borrower)) {
const kebabCaseKey = _.kebabCase(field.key);
const fieldElement = React.cloneElement(field.input, {
className: `${kebabCaseKey}-input`,
value: field.value ? field.value(customer, borrower) : _.get(borrower, field.key),
onChange: e => {
let value = e.target.value;
if (field.options) {
value = Number(value);
}
this.updateCustomer(fieldKey + field.key, value);
},
disabled: field.disabled(borrower),
...field.props(borrower, this)
}, field.options ? Object.keys(field.options).map(option => <option
key={option}
className={`${kebabCaseKey}-option`}
value={option}>
{field.options[option]}
</option>) : null);
return <div key={field.key} className={`value ${kebabCaseKey}`}>
<span>{field.text}</span>
{fieldElement}
</div>;
}
return null;
})
}
</div>
As you can see I use React.cloneElement to clone the input from the config file and assign new properties to it depends on what I get from the config file, in this case 4 custom props:
inputValue
loadOptions
onInputChange
onChange
My problem is that loadOptions is never called, any ideas why? On the other hand inputValue is assign correctly to the cityName state of the component.
My Problem was that REACT_ASYNC_SELECT_INPUT references normal Select and not Select.Async.
I am wanting to be able to select a language I have called "Skip" more than one time without it disappearing from the dropdown. Currently, any language I select will disappear from the dropdown. Is this possible? Here's my code:
const languages = [
{ key: 'skip', text: 'Skip', value: 'Skip' },
{ key: 'english', text: 'English', value: 'English' },
{ key: 'french', text: 'French', value: 'French' },
]
handleAudioInputChange = (e, { name, value }) => this.setState( { [name]: value })
<Form.Select fluid search label='Audio Channels' name='audiochannelsValue' value={audiochannelsValue} options={languages} placeholder='Choose Audio Channels' onChange={this.handleAudioInputChange} multiple = "true"/>
I've tried multiple things such as hideSelectedOptions = {false}, but this does not seem to work. Any ideas?
If you only want a string based on the user input, you could do:
handleAudioInputChange = (e, { value }) => {
this.setState(prevState => {
const newVal = `${prevState.audiochannelsValue.length ? '/' : ''}${value}`;
return {
textValue: prevState.textValue.concat(newVal),
audiochannelsValue: value
};
});
}
This will build a string based on the previous state and separate each value with /. Haven't tested it, but it should generate (in order):
English
English/Skip
English/Skip/French
English/Skip/French/Skip