How to change select options based on other selection ( Javascript) - javascript

I have an 4 Select component with the same list options. If I choose an option, that one will be remove from the list of other Select components so that no 2 Select have identical result. Here is the code:
listStaff = [
{id: 1, name: 'John Doe'},
{id: 2, name: 'Walter White},
{id: 3, name: 'Jesse Pinkman}
{id: 4, name: 'Saul Goodman}
{id: 5, name: 'Gus Fring}
{id: 6, name: 'Skyler White}
]
const [staff, setStaff] = useState([])
<Select
allowClear
placeholder="Choose staff #1"
onChange={(e) => onChangeStaff(e, 0)}
value={staff[0]}
>
{listStaff?.map((item) => {
return (
<Select.Option key={item?.id} value={item?.id}>
{item?.name}
</Select.Option>
);
})}
</Select>
<Select
allowClear
placeholder="Choose staff #2"
onChange={(e) => onChangeStaff(e, 1)}
value={staff[1]}
>
{listStaff?.map((item) => {
return (
<Select.Option key={item?.id} value={item?.id}>
{item?.name}
</Select.Option>
);
})}
</Select>
<Select
allowClear
placeholder="Choose staff #3"
onChange={(e) => onChangeStaff(e, 2)}
value={staff[2]}
>
{listStaff?.map((item) => {
return (
<Select.Option key={item?.id} value={item?.id}>
{item?.name}
</Select.Option>
);
})}
</Select>
<Select
allowClear
placeholder="Choose staff #4"
onChange={(e) => onChangeStaff(e, 3)}
value={staff[3]}
>
{listStaff?.map((item) => {
return (
<Select.Option key={item?.id} value={item?.id}>
{item?.name}
</Select.Option>
);
})}
</Select>
Here is what I try:
const onChangeStaff = (id, index) => {
let arrTemp = [...listStaff]
const i = arrTemp.findIndex(r => r.id == id)
if(i > -1 ){
arrTemp.splice(i, 1)
} else {
return arrTemp
}
setListStaff(arrTemp)
};
I can remove the chosen option from the list but when a clear the Select or choose another option, the previous one do not revert but lost permanently. So how can I remove one option when choosing but revert that one back when deselect? Thank you.
Demo:
https://codesandbox.io/s/holy-dew-h6qxc4?file=/src/App.js

i messing around with your code a bit. try this
import "./styles.css";
import { Select } from "antd";
import { useState } from "react";
export default function App() {
const [listStaff, setListStaff] = useState([
{ id: 1, name: "Walter White" },
{ id: 2, name: "Jesse Pinkman" },
{ id: 3, name: "Gus Fring" },
{ id: 4, name: "Saul Goodman" },
{ id: 5, name: "John Doe" },
{ id: 6, name: "Skyler White" }
]);
const [selectedStaff, setSelectedStaff] = useState(new Array(4).fill(null));
const handleChangeStaff = (id, i) => {
setSelectedStaff(p => {
const newSelectedStaff = [...p];
newSelectedStaff[i] = id === undefined ? null : listStaff.find(ls => ls.id === id);
return newSelectedStaff;
});
};
return (
<div className="App">
{new Array(4).fill("").map((_, i) => (
<Select
allowClear
placeholder={`Choose staff #${i+1}`}
onChange={(id) => handleChangeStaff(id, i)}
key={i}
>
{listStaff.filter(ls => selectedStaff.find(ss => ss !== null && ss.id === ls.id) === undefined).map(ls => (
<Select.Option key={ls.id} value={ls.id}>
{ls.name}
</Select.Option>
))}
</Select>
))}
</div>
);
}

Related

React-select multiple selects on one page

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>
)}

How to use selected attribute of select tag in a loop in React

It is easy when we have individual options like this:
<select name="phone_type" id="phone_type">
<option value="mobile" selected>Mobile</option>
<option value="home">Home</option>
<option value="office">Office</option>
</select>
But in my case I'm doing this using a loop:
phoneTypes = ['Mobile', 'Home', 'Office'];
...
<select onChange={(e) => this.handleChangePhonetype(e, index)}>
{this.phoneTypes.map((phoneType, index) => {
return (
<option key={index} value={phoneType} name="type">
{phoneType}
</option>);
})}
</select>
I searched the internet but couldn't get a proper answer for React. I want 'Mobile' to be selected already when the page loads. I don't know how to do this in React. Please pitch in.
Here is the stackblitz.
You should simply use the type of each phone object as the value to the select element.
Here's the updated stackblitz.
const PhoneTypes = ['Mobile', 'Home', 'Office'];
class Questionnaire extends Component {
state = {
phones: [{ type: 'Mobile', number: '' }],
};
addContact() {
this.setState((prevState) => ({
phones: [...prevState.phones, { type: 'Mobile', number: '' }],
}));
}
handleChange({ target: { name, value } }, phoneIndex) {
this.setState((prevState) => ({
phones: prevState.phones.map((phone, index) =>
phoneIndex === index ? { ...phone, [name]: value } : phone
),
}));
}
handleRemove(phoneIndex) {
this.setState((prevState) => ({
phones: prevState.phones.filter((_, index) => phoneIndex !== index),
}));
}
handleSubmit(e) {
console.log(this.state, '$$$');
}
render() {
return (
<div>
<h1>The Form</h1>
<label>Contact</label>
{this.state.phones.map((phone, index) => {
return (
<div key={index}>
<input
onChange={(e) => this.handleChange(e, index)}
value={phone.number}
name="number"
/>
<select
name="type"
value={phone.type}
onChange={(e) => this.handleChange(e, index)}
>
{PhoneTypes.map((phoneType, index) => {
return (
<option key={index} value={phoneType}>
{phoneType}
</option>
);
})}
</select>
<button onClick={(e) => this.handleRemove(index)}>Remove </button>
</div>
);
})}
<hr />
<button onClick={(e) => this.addContact(e)}>Add contact</button>
<hr />
<button onClick={(e) => this.handleSubmit(e)}>Submit</button>
</div>
);
}
}
I also fixed some other issues like mutating the state, incorrect arg to remove function etc.

How to add and edit list items in a dual list in reactjs?

I am pretty new to React js and trying different ways to make a to-do list to understand it further. I have a parent component that renders two child components. I figured out how to transfer the items between the two lists. How do I add items to the 2 lists separately from the UI? I am not able to figure that out. I need two input textboxes for each list and also should be able to edit the list items. Can anybody please help me?
import React,{useState,useEffect} from 'react'
import { Completed } from './Completed'
import { Pending } from './Pending'
export const Items = () => {
const [items,setItems]=useState([
{
id: 1,
title:'Workout',
status:'Pending'
},
{
id: 2,
title:'Read Books',
status:'Pending'
},
{
id: 3,
title:'Cook Pizza',
status:'Pending'
},
{
id: 4,
title:'Pay Bills',
status:'Completed'
},
{
id: 5,
title:' Watch Big Short',
status:'Completed'
},
{
id: 6,
title:' Make nutrition Plan',
status:'Pending'
}
])
const updateStatus=(id,newStatus)=>{
let allItems=items;
allItems=allItems.map(item=>{
if(item.id===id){
console.log('in here')
item.status=newStatus;
}
return item
})
setItems(allItems)
}
return (
<div class="items">
<Pending items={items} setItems={setItems} updateStatus={updateStatus}/>
<Completed items={items} setItems={setItems} updateStatus={updateStatus}/>
</div>
)
}
import React from 'react'
export const Completed = ({items,setItems,updateStatus}) => {
return (
<div className="completed">
<h1>RIGHT</h1>
{
items && items.map(item=>{
if(item && item.status==='Completed')
return <><p className="item" key={item.id}>{item.title} <button className="mark_pending" key={item.id} onClick={()=>{updateStatus(item.id,'Pending')}}> Move Left</button></p></>
})
}
</div>
)
}
import React from 'react'
export const Pending = ({items,setItems,updateStatus}) => {
return (
<div className="pending">
<h1>LEFT</h1>
{
items && items.map(item=>{
if(item && item.status==='Pending')
return <><p className="item" key={item.id}>{item.title} <button className="mark_complete" key={item.id} onClick={()=>{updateStatus(item.id,'Completed')}}>Move Right</button></p></>
})
}
</div>
)
}
What do you mean by "separately from the UI" ?
import React, { useState } from "react";
const initialStatus = "Pending";
const initialData = [
{
id: 1,
title: "Workout",
status: "Pending",
},
{
id: 2,
title: "Read Books",
status: "Pending",
},
{
id: 3,
title: "Cook Pizza",
status: "Pending",
},
{
id: 4,
title: "Pay Bills",
status: "Completed",
},
{
id: 5,
title: " Watch Big Short",
status: "Completed",
},
{
id: 6,
title: " Make nutrition Plan",
status: "Pending",
},
];
const Box = ({ id, title, status, setItems, items }) => {
return (
<button
onClick={() => {
const newItems = [...items];
const index = items.findIndex((v) => v.id == id);
newItems[index].status =
newItems[index].status == initialStatus ? "Completed" : initialStatus;
setItems(newItems);
}}
>
{title}
</button>
);
};
export const Items = () => {
const [items, setItems] = useState(initialData);
return (
<div style={{ display: "flex" }}>
<div style={{ display: "flex", flexDirection: "column" }}>
<h1>LEFT</h1>
{items
.filter((v) => v.status === initialStatus)
.map((props) => (
<Box {...props} key={props.id} setItems={setItems} items={items} />
))}
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<h1>Right</h1>
{items
.filter((v) => v.status !== initialStatus)
.map((props) => (
<Box {...props} key={props.id} setItems={setItems} items={items} />
))}
</div>
</div>
);
};
export default Items;

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>
);
});
})}

React render a select field multiple times

I have 5 labels that I need to render and each of them should have a select field with these options: string, fixed, guid, disabled
I implemented the following code
class renderCampaignCodes extends Component {
state = {
defaultCampaigns: [
{ name: 'Campaign Source: utm_source' },
{ name: 'Campaign Medium: utm_medium' },
{ name: 'Campaign Name: utm_campaign' },
{ name: 'Campaign Term: utm_term' },
{ name: 'Campaign Content: utm_content' }
],
defaultTypes: ['string', 'fixed', 'guid', 'disabled']
}
render() {
const { defaultCampaigns } = this.state
return (
<Flexbox flexDirection='column' marginTop='20px' width='600px'>
{
defaultCampaigns.map((campaign, idx) => {
return (
<div key={idx}>
<Flexbox justifyContent='space-between'>
<Flexbox marginTop='40px'>
<label>{campaign.name}</label>
</Flexbox>
<Flexbox>
<Field
name='type'
component={renderSelectField}
label='Type'
children={this.state.defaultTypes.map((item, i) => <MenuItem
key={i}
value={item}
label={item.replace(/^\w/, l => l.toUpperCase())}
primaryText={item.replace(/^\w/, l => l.toUpperCase())}/>)
}
/>
</Flexbox>
</Flexbox>
</div>
)
})
}
</Flexbox>
)
}
}
But the selected value from any of the select fields will show up everywhere:
How can I prevent this from happening?
It looks like the name attribute for each of your selects is being set to the same, causing them all to take the same value from store/state (depending on your implementation).
You will want to namespace the type with something. For example:
<Field
name={`${idx}.type`}
component={renderSelectField}
label='Type'
children={this.state.defaultTypes.map((item, i) => (
<MenuItem
key={i}
value={item}
label={item.replace(/^\w/, l => l.toUpperCase())}
primaryText={item.replace(/^\w/, l => l.toUpperCase())}
/>
))}
/>

Categories

Resources