How to disable `deselect-option` in `react-select`? - reactjs

I would like to use the react-select multi Select component and be able to select the same option multiple times. However, react-select does not allow one to do this. You can change the dropdown to show already selected options with hideSelectOptions={false}, but if you select one of them again, it will be deselected.
This issue #3234 describes this same problem and sugggests that one way to solve this problem is to handle the action argument to onChange somehow.
Here is the solution that I attempted based off of the suggested solution:
import React, { Component } from "react";
import Select from "react-select";
export default class MultiSelect extends Component<*, State> {
handleChange(option, action) {
console.log(option, action);
// return;
if (action === "deselect-option") {
action = "select-option";
// now how do I do the component's state?
}
}
render() {
return (
<Select
isMulti
className="basic-single"
classNamePrefix="select"
name="color"
options={[{"label": "hello", "value": "hello"}, {"label": "world", "value": "world"}]}
hideSelectedOptions={false}
onChange={this.handleChange}
/>
);
}
}
I expected to be able to enter "hello" multiple times but when I tried to enter "hello" again it was deleted.

In options, the data field makes value dynamic with any key, in this case, use Date.now() to make dynamic. then use actions 'select-option' to append to options, use actions 'remove-value'' to filter all fields in the Options data field with a label matching with selected option label and append to data and append one more object to Options datafield
options = [
{ value: Date.now(), label: "SUBJECT" ,val:"SUBJ" },
{ value: Date.now, label: "VERB", val:"VERB" }
]
<Select options={options} isMulti onChange={(e, option) => {
if (option.action === "select-option") {
Options=[
...Options,
{
value: option.option.value + "_" + Date.now(),
label: option.option.label
}
];
} else if (option.action === "remove-value" ||option.action === "pop-value") {
tempData = data.filter(opt => opt.label !== option.removedValue.label)
Options=[
...tempData,
{
value: option.removedValue.value + "_" + Date.now(),
label: option.removedValue.label
}
]
}
}
} />

Related

Use Antd select with mode tags but allow only one selection

I'm using Antd Select for a project. My requirement is to use Select with mode='tags' as it allows user to create a new option, but I also want user to select only one option at a time (i.e either have a new option created or have an existing option selected shown in the select box). I tried using autoClearSearchValue but is of no use.
Appreciate the help.
Make the <Select /> to be controlled component.
I write an example, the key logic is to control the selected value in onChange function;
export default function MyTest() {
const [data, setData] = React.useState<string[]>([]);
return (
<div>
<Select
mode="tags"
onChange={(value, options) => {
// update data only when select one item or clear action
if (options?.length === 0 || options?.length === 1) {
setData(value);
}
}}
value={data}
options={[
{
label: "0",
value: "0",
},
{
label: "1",
value: "1",
},
{
label: "2",
value: "2",
},
]}
/>
</div>
);
}

Access selected value from react's Select Component

Hello dear community,
I want to create a very basic website for my school assignment.
I have a dropdown menu with a Select Component as its implementation. I need to access the selected value, which is a currency in my case, in order to update the information displayed on the page once a currency has been selected.
I am kind of frustrated since I wasn't able to find a helpfull solution to my relatively basic problem (I think it's basic :D)
My component class here:
import { Component } from "react";
import Select from 'react-select';
interface DropdownMenuProps {
values: [{}]
defaultValue: string
}
interface DropdownMenuState { }
/**
* Represents a Dropdown Menu
*/
export default class DropdownMenu extends Component<DropdownMenuProps, DropdownMenuState> {
render() {
return (
<div style={{ width: '120px' }}>
<Select id="dropdown-menu"
placeholder={this.props.defaultValue}
options={this.props.values}
// getOptionValue={(option) => option.value}
// getOptionLabel={(option) => option.label}
/>
</div>
)
}
}
This is how I create a dropdown menu compent:
<DropdownMenu defaultValue="Currency" values={[{ label: "EUR", value: "EUR" }, { label: "GBP", value: "GBP" }, { label: "USD", value: "USD" }]} ></DropdownMenu>
I'm glad for any tips :)
You have to use the onChange prop of react-select:
<select
id="dropdown-menu"
onChange={handleSelectChange}
>
and in this function you could handle the changes:
const handleSelectChange = (selectedVal) => console.log(selectedVal)
I recommend you using directly onChange and save it into a state:
1st:
Create state with the name you want:
const [selectValue, setSelectValue] = useState({});
2nd: Create options:
const options = [
{ label: "Tomate", value: 1 },
{ label: "Queso", value: 2 }
];
3rd: add method onChange in select and pass options:
const onChange = (ev) => {
setSelectValue(ev);
//console.log(ev);
console.log(selectValue);
};
<Select
id="dropdown-menu"
placeholder={"e"}
options={options}
onChange={onChange}
/>
I have created demo here:
https://codesandbox.io/s/ancient-pond-96h53?file=/src/App.js

Unable to maintain formik state values within react-select component

I am using react-select (multi-values) with Formik together with Material-UI Stepper (wizard) and have the values stored successfully within Formik's initialStates values but when I advance to the next screen, using conditional component rendering within my Stepper and then return back one screen where my react-select component resides, it no longer holds/shows the values that have already been selected within my react-select component even though the values are still in Formik's initialStates values.
My state is as follows which is storing my selectedOptions correctly:
import Select from 'react-select';
const myOptions= [
{ value: 'Food', label: 'Food' },
{ value: 'Being Fabulous', label: 'Being Fabulous' },
{ value: 'Unicorns', label: 'Unicorns' },
{ value: 'Kittens', label: 'Kittens' },
];
"props": {
"myGroups": [
{
"myGroupName": "",
"selectedOptions": [
{
"value": "Unicorns",
"label": "Unicorns"
},
{
"value": "Kittens",
"label": "Kittens"
}
]
}
]
}
Here is the code for the react-select component:
<Formik initialValues={initialFormValues} validationSchema={formSchema} onSubmit={this.handleFormSubmit} enableReinitialize>
{({ handleSubmit, handleChange }) => (
<Form noValidate onSubmit={handleSubmit} autoComplete='off'>
<Select
options={myOptions}
isMulti={true}
name={`myGroups.${index}.selectedOptions`}
onChange={(selectedOption) => {
let e = { target: { name: `myGroups.${index}.selectedOptions`, value: selectedOption } };
handleChange(e);
}}
/>
</Form>
)}
</Formik>
When returning, I expected to see both "Unicorns" and "Kittens" within the select but it's empty.
Any ideas how I can maintain state within this component? Can I perhaps somehow use `defaultValue' ?
The only solution I found is to install this library : formik-material-ui
You can after easy import:
import { Select } from 'formik-material-ui';
And use it in the Field component props.

How to format value of the React Select

I use "react final forms" and "react select".
I've organized an interface and functionality works, but I have one thing which I don't like.
React select requires options in next format:
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
]
and the value also should be set as { value: 'chocolate', label: 'Chocolate' }.
But for me is strange to have in my model of data (and send to the server also) the value like this - { value: 'chocolate', label: 'Chocolate' }, because I need only 'chocolate'.
Easy way is format the object into a single value after the form will be saved and format back from single value to the object before the rendering of the element. I know how to do it outside of form, but in this case I should solve this problem again and again for each select element separately.
What I would like to do:
Find a way how to set value of the react select as single value, like 'chocolate' instead of object.
OR
Create a wrapper for react select component and format value there when it sets (it's easy) and when the form get the value from this field (this I don't know how to do).
I will appreciate any help.
Method 1
With strings:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Select from "react-select";
const data = ["1", "2"];
function SingleStringReactSelect() {
const [option, setOption] = useState();
return (
<section>
<Select
onChange={option => setOption(option)}
value={[option]}
getOptionLabel={label => label}
getOptionValue={value => value}
closeMenuOSelect={false}
options={data}
/>
</section>
);
}
Method 2:
Created example:
https://codesandbox.io/s/react-codesandboxer-example-z57ke
You can use map in options and with Wrapper like SingleValueReactSelect
import React, { Fragment, useState } from "react";
import Select from "react-select";
const data = ["chocolate", "strawberry", "vanilla"];
export function SingleValueReactSelect(props) {
const [selectedItem, setSelectedItem] = useState();
return (
<Select
{...props}
value={selectedItem}
onChange={item => {
setSelectedItem(item);
props.onChange(item.value);
}}
options={props.options.map(item => ({ label: item, value: item }))}
/>
);
}
export default function AppDemo() {
return (
<Fragment>
<p>SingleValueReactSelect Demo</p>
<SingleValueReactSelect
isClearable
isSearchable
options={data}
onChange={item => {
alert(item);
}}
/>
</Fragment>
);
}
You can transform/filter the object data once you are sending the form data to server or another component.
Setting the value of the react-select as a single value will not show it as selected in the React-Select.

React Redux - select all checkbox

I have been searching on Google all day to try and find a way to solve my issue.
I've created a "product selection page" and I'm trying to add a "select all" checkbox that will select any number of products that are displayed (this will vary depending on the customer).
It's coming along and I've got all the checkboxes working but I can't get "select all" to work. Admittedly I'm using some in-house libraries and I think that's what's giving me trouble as I'm unable to find examples that look like what I've done so far.
OK, so the code to create my checkboxGroup is here:
let productSelectionList = (
<FormGroup className="productInfo">
<Field
component={CheckboxGroup}
name="checkboxField"
vertical={true}
choices={this.createProductList()}
onChange={this.handleCheckboxClick}
helpText="Select all that apply."
label="Which accounts should use this new mailing address?"
/>
</FormGroup>
);
As you can see, my choices will be created in the createProductList method. That looks like this:
createProductList() {
const { products } = this.props;
const selectAllCheckbox = <b>Select All Accounts</b>;
let productList = [];
productList.push({ label: selectAllCheckbox, value: "selectAll" });
if (products && products.length > 0) {
products.forEach((product, idx) => {
productList.push({
label: product.productDescription,
value: product.displayArrangementId
});
});
}
return productList;
}
Also note that here I've also created the "Select All Accounts" entry and then pushed it onto the list with a value of "selectAll". The actual products are then pushed on, each having a label and a value (although only the label is displayed. The end result looks like this:
Select Products checkboxes
I've managed to isolate the "select all" checkbox with this function:
handleCheckboxClick(event) {
// var items = this.state.items.slice();
if (event.selectAll) {
this.setState({
'isSelectAllClicked': true
});
} else {
this.setState({
'isSelectAllClicked': false
});
}
}
I also created this componentDidUpdate function:
componentDidUpdate(prevProps, prevState) {
if (this.state.isSelectAllClicked !== prevState.isSelectAllClicked && this.state.isSelectAllClicked){
console.log("if this ", this.state.isSelectAllClicked);
console.log("if this ", this.props);
} else if (this.state.isSelectAllClicked !== prevState.isSelectAllClicked && !this.state.isSelectAllClicked){
console.log("else this ", this.state.isSelectAllClicked);
console.log("else this ", this.props);
}
}
So in the console, I'm able to see that when the "select all" checkbox is clicked, I do get a "True" flag, and unclicking it I get a "False". But now how can I select the remaining boxes (I will admit that I am EXTREMELY new to React/Redux and that I don't have any previous checkboxes experience).
In Chrome, I'm able to see my this.props as shown here..
this.props
You can see that this.props.productList.values.checkboxField shows the values of true for the "select all" checkbox as well as for four of the products. But that's because I manually checked off those four products for this test member that has 14 products. How can I get "check all" to select all 14 products?
Did I go about this the wrong way? (please tell me that this is still doable) :(
My guess is your single product checkboxes are bound to some data you have in state, whether local or redux. The checkbox input type has a checked prop which accepts a boolean value which will determine if the checkbox is checked or not.
The idea would be to set all items checked prop (whatever you are actually using for that value) to true upon clicking the select all checkbox. Here is example code you can try and run.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
state = {
items: [
{
label: "first",
checked: false,
},
{
label: "last",
checked: false,
}
],
selectAll: false,
}
renderCheckbooks = (item) => {
return (
<div key={item.label}>
<span>{item.label}</span>
<input type="checkbox" checked={item.checked} />
</div>
);
}
selectAll = (e) => {
if (this.state.selectAll) {
this.setState({ selectAll: false }, () => {
let items = [...this.state.items];
items = items.map(item => {
return {
...item,
checked: false,
}
})
this.setState({ items })
});
} else {
this.setState({ selectAll: true }, () => {
let items = [...this.state.items];
items = items.map(item => {
return {
...item,
checked: true,
}
})
this.setState({ items })
});
}
}
render() {
return (
<div className="App">
{this.state.items.map(this.renderCheckbooks)}
<span>Select all</span>
<input onClick={this.selectAll} type="checkbox" checked={this.state.selectAll} />
</div>
);
}
}
export default App;
I have items in state. Each item has a checked prop which I pass to the checkbox getting rendered for that item. If the prop is true, the checkbox will be checked otherwise it wont be. When I click on select all, I map thru my items to make each one checked so that the checkbox gets checked.
Here is a link to a codesandbox where you can see this in action and mess with the code.
There is a package grouped-checkboxes which can solve this problem.
In your case you could map over your products like this:
import React from 'react';
import {
CheckboxGroup,
AllCheckerCheckbox,
Checkbox
} from "#createnl/grouped-checkboxes";
const App = (props) => {
const { products } = props
return (
<CheckboxGroup onChange={console.log}>
<label>
<AllCheckerCheckbox />
Select all accounts
</label>
{products.map(product => (
<label>
<Checkbox id={product.value} />
{product.label}
</label>
))}
</CheckboxGroup>
)
}
More examples see https://codesandbox.io/s/grouped-checkboxes-v5sww

Resources