React Selecting dropdown option multiple times - reactjs

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

Related

Update nested React state?

I'm trying to update part of a state object that is nested. This is the object:
const [buttonObject, setButtonObject] = useState({
intro: [
{
id: '123',
name: 'first_intro_name',
selected: false,
},
{
id: '124',
name: 'second_intro_name',
selected: false,
},
],
experience: [
{
id: '789',
name: 'first_experience_name',
selected: false,
},
{
id: '8910',
name: 'second_experience_name',
selected: false,
},
],
});
When a button is clicked I want to toggle the selected state. I'm using a click handler that looks like this:
const handleButtonClick = ({ id, selected }) => {
if (id === '123') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
} else if (id === '124') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
}
};
It would handle experiences as well. The issue is that right now it seems like rather than updating the object it just overwrites the object or creates a new one. It also doesnt pass that information back down to the component even though I have it routed correctly so it should.
Is there better/correct syntax for updating nested state like this?
Thanks.
instead of checking again with if condition use higher order array function and spread operator to get optimal solution.
setButtonObject(previous => {
return {
...previous,
info: previous.info.map(item => item.id === id ? {
...item,
selected: true
} ? item)
}
})

Semantic UI dropdown not setting value after selecting option

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

react-tag-autocomplete with redux form

Im trying to use react-tag-autocomplete with redux form.
This is the documentation for react-tag-autocomplete
React-tag-autocomplete
And below is react-tag-autocomplete with redux-form
I just wanted to know whether it's possible to display the id number along with the value (fruit name) in the list. See image below (red number refers to the id). I've gone through the documentation but i can't seem to find anything that deals with this. What i ultimately want to achieve is something like stackoverflow's tag system with id number and description.
You can use suggestionComponent prop to customise suggestion based on your needs.
Imp note - suggestionComponent is still in beta version. You have to update your library to version 6.
See Working copy of your code here (with version 6)
Code snippet
function SuggestionComponent({ item, query }) {
return (
<span id={item.id}>
{item.name} - {item.id}
</span>
);
}
const TagAutocomplete = ({ input: { value, onChange } }) => {
const suggestions = [
{ id: 1, name: "Apples" },
{ id: 2, name: "Pears" },
{ id: 3, name: "Bananas" },
{ id: 4, name: "Mangos" },
{ id: 5, name: "Lemons" },
{ id: 6, name: "Apricots" }
];
const newValue = !value ? [] : value;
const handleDelete = i => {
const tags = [...newValue];
tags.splice(i, 1);
onChange(tags);
};
const handleAdd = e => {
console.log("e", e);
onChange([...newValue, e]);
};
return (
<ReactTags
suggestionComponent={SuggestionComponent}
tags={newValue}
suggestions={suggestions}
handleDelete={handleDelete}
handleAddition={handleAdd}
/>
);
};

Is it possible to save the order of the column of material table to a database to keep it persistent?

I want my users to be able to drag the columns on the material table and keep that state persistent. I can get the states of my columns and their orders at any time. I do save them. However, when I try to render with the saved order of my columns it doesn't seem to update my material table.
I'm using react Material-Table from material-table.com
Here is the code I'm trying to do:
const tableHeaders = [];
tableHeaders.push({ title: 'a', field: 'a' });
tableHeaders.push({ title: 'b', field: 'b' });
tableHeaders.push({ title: 'c', field: 'c' });
const Table = (props) => {
const [state, setState] = useState({
headers: tableHeaders,
data: data // some data
});
const handleColumnDragged = (start, end) => {
// process the order of columns
// create as an array i.e. order = [2, 1, 0]
updateOrder(order);
// save to database
}
const updateHeaderOrder = (order) => {
let newHeaders = [];
order.forEach( i => {
newHeaders.push(tableHeaders[i]);
});
setState({ ...state, headers: newHeaders });
}
useEffect(() => {
resource.fetchHeaderOrder( order => {
updateHeaderOrder(order);
});
}, [resource]);
return (
<MaterialTable
columns={state.headers}
data={state.data}
onColumnDragged={handleColumnDragged}
/>
}
Is it possible to do this or not? Is material designed to only have the same column order everytime? It's weird that they allow drag event though if that is the case?
I use the following approach to store the column order in localStorage. You can enhance it to store column order in the database.
export default function Monitor() {
let columns = [];
let data = [...]
// Checking if key exists in local storage
if (localStorage.getItem('table1') === null) {
// If key doesn't exists then store the actual column order in local storage
localStorage.setItem(
'table1',
JSON.stringify({
0: { title: 'Avatar', field: 'avatar', removable: false },
1: { title: 'First Name', field: 'first_name' },
2: { title: 'Last Name', field: 'last_name' },
3: { title: 'Last Change', field: 'lastChange' },
4: { title: 'Last Changed By', field: 'lastChangedBy' }
})
);
}
let savedColumns = JSON.parse(localStorage.getItem('table1'));
for (var columnIndex in savedColumns) {
columns.push(savedColumns[columnIndex]);
}
function handleColumnDrag(sourceIndex, destinationIndex) {
const sourceColumn = savedColumns[sourceIndex];
const destinationColumn = savedColumns[destinationIndex];
// Swapping the column order
savedColumns[sourceIndex] = destinationColumn;
savedColumns[destinationIndex] = sourceColumn;
localStorage.setItem('table1', JSON.stringify(savedColumns));
}
return (
<MaterialTable
columns={columns}
data={data}
onColumnDragged={handleColumnDrag}
icons={tableIcons}
/>
);
}
You can store the order on the server in the database or locally in the localStorage, but sustaining the persistence is fully up to you.
On the other hand material-table will render the columns in the order declared by you. Doesn't the order change if you push headers in the different order at the top? E.g:
tableHeaders.push({ title: 'c', field: 'c' });
tableHeaders.push({ title: 'b', field: 'b' });
tableHeaders.push({ title: 'a', field: 'a' });

Passing options with multiple values that are the same in to React-select

Below is a snippet of how I call the Select menu:
import Select from 'react-select';
const SelectMenu = (props) => {
const { options, defaultValue, onChange, name, label } = props;
return (
<Select
options={options}
defaultValue={defaultValue}
onChange={onChange}
name={name}
id="search-select"
/>
);
};
My options object looks as follows:
TestObj: [
{
label: 'Test 1',
value: 3.49
},
{
label: 'Test 2',
value: 3.99
},
{
label: 'Test 3',
value: 3.89
},
{
label: 'Test 4',
value: 3.99
},
{
label: 'Test 5',
value: 0
}
]
Because of the fact two of the options share the same value I get this effect:
As can be seen due to Test 2 and 4 sharing the same value they both show as selected.
Is there a way that whilst keeping the same values I can have this so that it only selects the actual selected option?
The way react-select by default checks if a value is selected is by extracting the value piece and comparing it to whats selected (an array of objects with a value key.)
Using isOptionSelected you can override the strategy employed:
isOptionSelected={(option, selectValue) => selectValue.some(i => i === option)}
This is more or less how the internal function works without extracting the value part, and instead compares the whole object.

Resources