React-select multiple selects on one page - javascript

I am a bit confused, here is an example with a couple of select inputs that have the same state, please check here: https://stackblitz.com/edit/get-selected-by-value-multi-select-react-agamk4?file=src/App.js so please:
How can I make it so when I select an option the value does not apply to the rest of the select inputs?
How would you put the values in the store for each of the selects?
Do I need multiple stores?
For more clarity, here is a screenshot: https://www.awesomescreenshot.com/image/19798040?key=bb839c650c93b436066e03d33d5515b0 I hope this makes sense? What would be the best approach? Thank you.

I have shared the code in case of only a single state. You can use this method if you want only a single state but having multiple states for different select inputs also won't be bad as you have only 3 inputs. Having single state method would be useful if number of select inputs would have more.
import React, { useState } from 'react';
import Select from 'react-select';
function App() {
const data = [
{
value: 1,
label: 'cerulean',
},
{
value: 2,
label: 'fuchsia rose',
},
{
value: 3,
label: 'true red',
},
{
value: 4,
label: 'aqua sky',
},
{
value: 5,
label: 'tigerlily',
},
{
value: 6,
label: 'blue turquoise',
},
];
// set value for default selection
const [selectedValue, setSelectedValue] = useState([
{ value: [] },
{ value: [] },
{ value: [] },
]);
// handle onChange event of the dropdown
const handleChange = (e, no) => {
setSelectedValue(
selectedValue.map((item) => {
return selectedValue.indexOf(item) === no
? { value: Array.isArray(e) ? e.map((x) => x.value) : [] }
: item;
})
);
};
return (
<div className="App">
<Select
className="dropdown"
placeholder="Select Option"
value={data.filter((obj) => selectedValue[0].value.includes(obj.value))} // set selected values
options={data} // set list of the data
onChange={(event) => handleChange(event, 0)} // assign onChange function
isMulti
isClearable
/>
<br />
<Select
className="dropdown"
placeholder="Select Option"
value={data.filter((obj) => selectedValue[1].value.includes(obj.value))} // set selected values
options={data} // set list of the data
onChange={(event) => handleChange(event, 1)} // assign onChange function
isMulti
isClearable
/>
<br />
<Select
className="dropdown"
placeholder="Select Option"
value={data.filter((obj) => selectedValue[2].value.includes(obj.value))} // set selected values
options={data} // set list of the data
onChange={(event) => handleChange(event, 2)} // assign onChange function
isMulti
isClearable
/>
{selectedValue && (
<div style={{ marginTop: 20, lineHeight: '25px' }}>
<div>
<b>Selected Value: </b> {JSON.stringify(selectedValue, null, 2)}
</div>
</div>
)}
</div>
);
}
export default App;

{selectedValue && (
<div style={{ marginTop: 20, lineHeight: '25px' }}>
<div>
<b>Selected Values: </b>
<span>{
selectedValue.map(item => item.value.length !== 0 ?
<li>{data.filter(data => data.value === item.value[0])[0].label}</li> :
<li>No value selected</li>
)
}</span>
</div>
</div>
)}

Related

values object is not updated while using react-select inside field array

Since I want my variation form to be dynamic, I have used the concept of field array from react-hook-form. For the select field I am using react-select which is used for taking multiple values for a particular option from the user. This is how I have done
import React from "react";
import { useForm, useFieldArray } from "react-hook-form";
import ReactSelect from "./Select";
import Input from "./Input";
import VariationPreview from "./VariationPreview";
const initialValues = {
variations: [
{
id: 1,
option: "Size",
values: [
{ id: 1, label: "SM", value: "sm" },
{ id: 2, label: "MD", value: "md" }
]
},
{
id: 2,
option: "Color",
values: [
{ id: 1, label: "Red", value: "red" },
{ id: 2, label: "Blue", value: "blue" }
]
}
]
};
const VariationSetup = () => {
const {
register,
control,
handleSubmit,
formState: { errors },
watch,
getValues
} = useForm({
defaultValues: initialValues ?? {}
});
const { fields, append, remove } = useFieldArray({
control, // control props comes from useForm (optional: if you are using FormContext)
name: "variations" // unique name for your Field Array
});
const variants = watch("variations");
console.log("variants", variants);
return (
<>
<p className="font-semibold mb-4 text-md">Options</p>
{fields.map((field, index) => {
return (
<React.Fragment key={field.id}>
<div className="flex mb-4" key={field.id}>
<div className="w-full md:w-4/12">
<Input
name={`variations.${index}.option`}
label="Option"
placeholder="Choose option"
options={VARIATION_OPTION}
helperText="Choose option that can be applied as variants for a product"
errors={errors}
register={register}
/>
</div>
<div className="w-full md:ml-4 md:w-8/12">
<ReactSelect
name={`variations.${index}.values`}
label=""
placeholder="Choose value"
options={[]}
helperText="You can choose multiple values"
wrapperClassName="mt-7"
errors={errors}
register={register}
isCreateable
/>
</div>
</div>
</React.Fragment>
);
})}
<button
className="bg-gray-200 p-3"
type="button"
onClick={() => append({ option: "", value: "" })}
>
Add option
</button>
<div className="divide-y"></div>
{/* PREVIEW */}
<VariationPreview variations={watch("variations")} />
</>
);
};
export default VariationSetup;
const VARIATION_OPTION = [
{ id: 1, label: "Size", value: "size" },
{ id: 2, label: "Color", value: "color" }
];
this is the react-select with hook-form binding
import { Controller, useForm } from "react-hook-form";
import Select from "react-select";
import CreatableSelect from "react-select/creatable";
export default function ReactSelect({
disabled,
label,
helperText,
name,
placeholder,
options,
defaultValue,
className,
labelClassName,
wrapperClassName,
isCreateable = false
}) {
const {
control,
formState: { errors }
} = useForm();
const getDefaultValue = (value) => {
if (value && value.length) {
return value[0];
} else return value;
};
return (
<div className={wrapperClassName ? wrapperClassName : ""}>
{/* TODO: Label as a separate component */}
<label
htmlFor={name}
className={
labelClassName
? labelClassName
: "block font-semibold mb-2 text-gray-700 text-sm tracking-wide uppercase"
}
>
{label}
</label>
<div className="mt-1 relative">
<Controller
name={name}
defaultValue={getDefaultValue(defaultValue)}
control={control}
render={({ field }) => {
const styles = errors[name] ? errorStyles : customStyles;
if (isCreateable) {
return (
<CreatableSelect
isMulti
{...field}
isDisabled={disabled}
placeholder={placeholder}
options={options}
styles={styles}
className={className}
/>
);
} else {
return (
<Select
{...field}
isDisabled={disabled}
placeholder={placeholder}
options={options}
styles={styles}
className={className}
/>
);
}
}}
/>
</div>
</div>
);
}
Here the problem is when I update values field the variations object does not get updated because of which I cannot update Variation Preview table. Also when if I add new option, the option key gets updated and is reflected in VariationPreview table but when I add values for that option the values object is shown empty. Could anyone point me where i did the mistake? I have a code in playground either
https://codesandbox.io/s/elegant-robinson-fiujlj?file=/src/Variation.jsx:0-2764
You call useForm inside the ReactSelect component. Now there are two unrelated forms. That's why variations object does not get updated.
You can pass the control down to ReactSelect and use it for Controller component.
And for errors handling:
const { errors } = useFormState({
control
});

How to take out props data from a component in react-js?

I am working on a react.js app with react-select using which I have made one dropdown menu and on click of an item in menu I want to pass that item to a function which is later connected to redux store.How can I access data from a component that is used in react-select?
Here's my code that will give you more reference.
const Option = (props) => {
return (
<components.Option {...props} >
<div>{props.data.api}</div>
<div style={{ fontSize: 12 }}>{props.data.group}</div>
</components.Option>
);
};
The above code is my Option component which is used below to render a Select Menu.
return (
<StyledForm id="form_container">
<Grid>
<Grid.Row>
<Grid.Column width={3}>
<input
label="Client Name"
value={this.props.clientName}
onChange={this.setClientName}
/>
<Select options={this.props.clientGrantList} components={{ Option }} onChange={()=>this.addApiGrants(//how to pass data)} />
</Grid.Column>
This is my UI component where select menu is showing.
In the below Line in addApiGrants function I want to pass in select option data.How can I do it?
<Select options={this.props.clientGrantList} components={{ Option }} onChange={()=>this.addApiGrants(//how to pass data)} />
Thanks
The onChange handler function receives selected object as an argument. So, the code to handle this would be :
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
export default function App() {
return (
<div className="App">
<Select options={options} onChange={item => addApiGrants(item)} />
</div>
);
}
This is how the selected item from onChange handler looks like
{value: "strawberry", label: "Strawberry"}

Multiple time slots for Week days using ReactJS

If anyone can help me to optimize the following code. I am trying to create a registration page where users can select their time availability for selected days. Users have option to select multiple rows for the same day.. The link for codesandbox is https://codesandbox.io/s/react-hook-helper-availability-0r6bd?file=/src/Second.js. Though i have achieved this but it can be further be optimized as i am using the same code for different days. I am reusing the same code. I have added for just Monday and Tuesday, in case i have to use Monday to Saturday, then i will have to repeat the same codes with changes in few fields.
const [monday, setMonday] = useState([{ FROM: "", TO: "" }]);
const [tuesday, setTuesday] = useState([{ FROM: "", TO: "" }]);
const [time, setTime] = useState([
{ Id: "00:30", value: "00:30" },
{ Id: "01:00", value: "01:00" },
{ Id: "01:30", value: "01:30" },
{ Id: "02:00", value: "02:00" },
......
let timeList =
time.length > 0 &&
time.map((item, i) => {
return (
<>
<option key={item.Id} value={item.id}>
{item.value}
</option>
</>
);
}, this);
On add, remove actions
const handleInputChangeForMonday = (e, index) => {
const { name, value } = e.target;
const list = [...monday];
list[index][name] = value;
setMonday(list);
};
// handle click event of the Remove button
const handleRemoveClickForMonday = (index) => {
const list = [...monday];
list.splice(index, 1);
setMonday(list);
};
// handle click event of the Add button
const handleAddClickForMonday = () => {
setMonday([...monday, { FROM: "", TO: "" }]);
};
// handle input change
const handleInputChangeForTuesday = (e, index) => {
const { name, value } = e.target;
const list = [...tuesday];
list[index][name] = value;
setTuesday(list);
};
// handle click event of the Remove button
const handleRemoveClickForTuesday = (index) => {
const list = [...tuesday];
list.splice(index, 1);
setTuesday(list);
};
// handle click event of the Add button
const handleAddClickForTuesday = () => {
setTuesday([...tuesday, { FROM: "", TO: "" }]);
};
Now this is the repeated code.
<form onSubmit={onSubmit}>
{monday.map((x, i) => {
return (
<React.Fragment>
<select
name="FROM"
value={x.FROM}
onChange={(e) => handleInputChangeForMonday(e, i)}
>
<option selected hidden>
From
</option>
{timeList}
</select>
<select
name="TO"
value={x.TO}
onChange={(e) => handleInputChangeForMonday(e, i)}
placeholder="select your Institute"
>
<option selected hidden>
TO
</option>
{timeList}
</select>
<div style={{ textAlign: "left", width: "84%" }}>
{monday.length !== 1 && (
<label
as="a"
onClick={() => handleRemoveClickForMonday(i)}
style={{ marginRight: "10px" }}
>
remove
</label>
)}
{monday.length - 1 === i && (
<button
type="button"
as="a"
onClick={handleAddClickForMonday}
style={{ marginRight: "10px" }}
>
add
</button>
)}
</div>
</React.Fragment>
);
})}
<br />
<br />
{tuesday.map((x, i) => {
return (
<React.Fragment>
<select
name="FROM"
value={x.FROM}
onChange={(e) => handleInputChangeForTuesday(e, i)}
>
<option selected hidden>
From
</option>
{timeList}
</select>
<select
name="TO"
value={x.TO}
onChange={(e) => handleInputChangeForTuesday(e, i)}
placeholder="select your Institute"
>
<option selected hidden>
TO
</option>
{timeList}
</select>
<div style={{ textAlign: "left", width: "84%" }}>
{tuesday.length !== 1 && (
<label
as="a"
onClick={() => handleRemoveClickForTuesday(i)}
style={{ marginRight: "10px" }}
>
remove
</label>
)}
{tuesday.length - 1 === i && (
<button
type="button"
as="a"
onClick={handleAddClickForTuesday}
style={{ marginRight: "10px" }}
>
add
</button>
)}
To reduce redundancy you can look at what code is repeated and think of it more abstractly.
For example, the following code abstractly copies the entries for a day, removes an element, and updates state with new array for that day
// handle click event of the Remove button
const handleRemoveClickForMonday = (index) => {
const list = [...monday];
list.splice(index, 1);
setMonday(list);
};
Now that you can abstractly operate on any day, think of a data structure that lends itself to looking up a specific day to operate on, like a map. In javascript it is common to use an object ({}) as a map of key-value pairs.
Convert state to object with day keys
const [days, setDays] = useState({
monday: [{ FROM: "", TO: "" }],
tuesday: [{ FROM: "", TO: "" }],
wednesday: [{ FROM: "", TO: "" }],
thursday: [{ FROM: "", TO: "" }],
friday: [{ FROM: "", TO: "" }],
saturday: [{ FROM: "", TO: "" }],
sunday: [{ FROM: "", TO: "" }],
});
Update mounting effect hook (probably room for improvement here as well since just initializing data really; I didn't dig in on what the AVAILABILITY_XXX's were)
useEffect(() => {
if (AVAILABILITY_MONDAY.length > 0)
setDays((days) => ({
...days,
monday: AVAILABILITY_MONDAY
}));
if (AVAILABILITY_TUESDAY.length > 0)
setDays((days) => ({
...days,
tuesday: AVAILABILITY_TUESDAY
}));
// etc for each day of the week
}, []);
Convert submit handler to access new state shape
const onSubmit = (data) => {
const e = {
target: {
name: "AVAILABILITY_MONDAY",
value: days.monday
}
};
const f = {
target: {
name: "AVAILABILITY_TUESDAY",
value: days.tuesday
}
};
// etc for each day
setForm(e);
setForm(f);
// etc
navigation.next();
};
Convert handlers to take a day key
// handle input change
const handleInputChangeForDay = (e, day, index) => {
const { name, value } = e.target;
const list = [...days[day]];
list[index][name] = value;
setDays((days) => ({
...days,
[day]: list
}));
};
// handle click event of the Remove button
const handleRemoveClickForDay = (day, index) => {
const list = [...days[day]];
list.splice(index, 1);
setDays((days) => ({
...days,
[day]: list
}));
};
// handle click event of the Add button
const handleAddClickForDay = (day) => () => {
setDays((days) => ({
...days,
[day]: [...days[day], { FROM: "", TO: "" }]
}));
};
Create an array of key-value pairs from state and map each day
{Object.entries(days).map(([dayKey, day]) => {
return day.map((x, i) => {
return (
<React.Fragment>
Day: {dayKey}
<select
name="FROM"
value={x.FROM}
onChange={(e) => handleInputChangeForDay(e, dayKey, i)}
>
<option selected hidden>
From
</option>
{timeList}
</select>
<select
name="TO"
value={x.TO}
onChange={(e) => handleInputChangeForDay(e, dayKey, i)}
placeholder="select your Institute"
>
<option selected hidden>
TO
</option>
{timeList}
</select>
<div style={{ textAlign: "left", width: "84%" }}>
{day.length !== 1 && (
<label
as="a"
onClick={() => handleRemoveClickForDay(dayKey, i)}
style={{ marginRight: "10px" }}
>
remove
</label>
)}
{day.length - 1 === i && (
<button
type="button"
as="a"
onClick={handleAddClickForDay(dayKey)}
style={{ marginRight: "10px" }}
>
add
</button>
)}
</div>
</React.Fragment>
);
});
})}

How to display and handle dynamic checkoxes that are dependent on Task array value in the backend (Mongodb) in react js?

I working on a react project where I have requirement like,
I have array inside contain, 1 Object and 1 Array named Task[]
"contractor": [
{
"contractGivenBy": -1,
"contractorID": 0,
"contractorName": "contractor1",
"reviewedByAssigner": false,
"submitReviewToAssigner": false,
"tasks": [ 2, 4, 6 ],
"tasksDone": false
},
Now, I want to display the Tasks array as Checkboxes in the page.
That is nice, I displayed all checkboxes using map() method, But the problem is, How to handle (get values from those checkboxes) when user checked or unchecked the specific checkbox.
I'm using React functional component with React hooks.
Here is what is tried..
<form onSubmit={onSubmitHandler}>
{
projectData.contractor[0].tasks.map((task, index) => {
return (
<div style={{ flexDirection: "column" }}>
<FormControlLabel
control={
<Checkbox
checked={false}
value={task}
onChange={handleTask} />
}
label={`task ${task}`}
/>
</div>
)
})
}
<Button
type="submit"
style={{
backgroundColor:"rgba(25,123,189)",
color: "white"
}}>
Assgin
</Button>
</form>
UPDATED
Here you go , it uses react hooks with checkbox implementation, i have tweaked it a little with <input type /> but you will get the idea
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Checkbox = ({ type = "checkbox", name, checked = false, onChange }) => {
console.log("Checkbox: ", name, checked);
return (
<input type={type} name={name} checked={checked} onChange={onChange} />
);
};
const CheckboxExample = () => {
const [checkedItems, setCheckedItems] = useState({});
const handleChange = event => {
setCheckedItems({
...checkedItems,
[event.target.name]: event.target.checked
});
console.log("checkedItems: ", checkedItems);
};
const checkboxes = [
{
name: "check-box-1",
key: "checkBox1",
label: "Check Box 1"
},
{
name: "check-box-2",
key: "checkBox2",
label: "Check Box 2"
}
];
return (
<div>
<lable>Checked item name : {checkedItems["check-box-1"]} </lable> <br />
{checkboxes.map(item => (
<label key={item.key}>
{item.name}
<Checkbox
name={item.name}
checked={checkedItems[item.name]}
onChange={handleChange}
/>
</label>
))}
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<CheckboxExample />, rootElement);

Retrieving the key for the entry from material-ui autocomplete at onSelect, instead of value

I'm using React with Material-ui and the Autocomplete component documented here - https://material-ui.com/components/autocomplete/ with downshift.
<Downshift id="downshift-options">
{({
clearSelection,
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
highlightedIndex,
inputValue,
isOpen,
openMenu,
selectedItem,
}) => {
const {onSelect, onBlur, onChange, onFocus, ...inputProps} = getInputProps({
onChange: event => {
if (event.target.value === '') {
clearSelection();
}
},
onSelect: event => {
if (event.target.id) {
this.props.onSelect(event.target.value);
}
},
onFocus: openMenu,
placeholder: 'Type to search',
});
return (
<div className={classes.container}>
{renderInput({
fullWidth: true,
classes,
label: "Assigned Rider",
InputLabelProps: getLabelProps({shrink: true}),
InputProps: {onBlur, onChange, onFocus, onSelect},
inputProps,
})}
<div {...getMenuProps()}>
{isOpen ? (
<Paper className={classes.paper} square>
{getSuggestions(this.props.suggestions, inputValue, {showEmpty: true}).map((suggestion, index) =>
renderSuggestion({
suggestion,
index,
itemProps: getItemProps({item: suggestion.label}),
highlightedIndex,
selectedItem,
}),
)}
</Paper>
) : null}
</div>
</div>
);
}}
</Downshift>
With onSelect, I can retrieve the value of the selection. I'd like to be able to retrieve the key instead.
function renderSuggestion(suggestionProps) {
const { suggestion, index, itemProps, highlightedIndex, selectedItem } = suggestionProps;
const isHighlighted = highlightedIndex === index;
const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;
return (
<MenuItem
{...itemProps}
key={suggestion.uuid}
value={suggestion.uuid}
selected={isHighlighted}
component="div"
style={{
fontWeight: isSelected ? 500 : 400,
}}
>
{suggestion.label}
</MenuItem>
);
}
Here I set the uuid as the key for each selection.
My ultimate aim is to be able to make a selection and retrieve a uuid instead of the value itself.
Although I can use the value returned to match against a list of items, I want to be sure that if there end up being any duplicate entries, it doesn't cause problems.
Link to my full code for the component is here - https://github.com/theocranmore/bloodbike/blob/master/react_app/src/components/UsersSelect.js#L143
Thank you!
I don't know why you need an id but for my own getting the object itself will suffice.
<Autocomplete .....
onChange={(event, newValue) => {
console.log(newValue); //this will give you the selected value dictionary (source)
}}
You can retrieve the entire value and then access your desired key (option.id):
const options = [
{ id: 0, value: "foo" },
{ id: 1, value: "goo" },
];
<Autocomplete
options={options}
onChange={(event, option) => {
console.log(option.id); // 1
}}
renderInput={(params) => <TextField {...params} />}
/>;

Categories

Resources