How can I load multi select dynamically? [react-select] - reactjs

How can I load Multi-Select dynamically?
I used react-select to implement MultiSelect.
My Efforts
In componentDidMount(), I fetched an array, which I want to display/load in my multi-select; then a response is stored in state.
Now, I tried to get value from that state, but I didn't get that value.
My Code
state= {Category: []}
// that category contain this values
//0: {categoryid: "1", categoryname: "Select Category"}
//1: {categoryid: "2", categoryname: "Abc"}
componentDidMount() {
fetch("http://myURL//file.php", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(responseJson => {
this.setState({ Category: responseJson });
// If server response message same as Data Matched
console.log(this.state.Category);
window.parent.location = window.parent.location.href;
})
.catch(error => {
console.error(error);
});
}
//this code is not working, display nothing
<Select
closeMenuOnSelect={false}
components={animatedComponents}
isMulti
>
{this.state.Category.map((e, key) => {
return (
<option key={key} value={e.categoryid}>
{e.categoryname}
</option>
);
})}
</Select>
Please help me with this problem

react-select has options props.
<Select
closeMenuOnSelect={false}
components={animatedComponents}
options={this.state.Category.map(e => ({ label: e.categoryname, value: e.categoryid}))}
isMulti
onChange={newValue => this.setState({ selected: newValue })}
/>

How can I select values of this multi-select based on another select
component?
You can store selected values for both selects in state and filter options based on selected value.
I added quick sample with 2 dependent selects - Hospital (can have few doctors) and Doctor (can work in few hospitals).
When you select some Doctor - Hospital selection is updated and vice-versa.
Preview this code
import React, { useState } from "react";
import { render } from "react-dom";
import Select from "react-select";
const data = {
doctors: [
{
id: 1,
name: "Andrew",
hospitals: [{ id: 1, title: "Test Hospital" }, { id: 2, title: "Test2" }]
},
{
id: 2,
name: "Another",
hospitals: [{ id: 1, title: "Test Hospital" }, { id: 3, title: "Test3" }]
}
],
hospitals: [
{ id: 1, title: "Test Hospital" },
{ id: 2, title: "Test2" },
{ id: 3, title: "Test3" }
]
};
function App() {
const [selectedDoctor, setSelectedDoctor] = useState(null);
const [selectedHospital, setSelectedHospital] = useState(null);
const hospitalOption = item => ({ value: item.id, label: item.title });
const hospitalOptions = () => {
if (selectedDoctor) {
return data.doctors
.filter(doctor => doctor.id === selectedDoctor.value)[0]
.hospitals.map(hospitalOption);
} else {
return data.hospitals.map(hospitalOption);
}
};
const doctorOption = item => ({
value: item.id,
label: `Doctor ${item.name}`
});
const doctorOptions = () => {
if (selectedHospital) {
return data.doctors
.filter(
doctor =>
doctor.hospitals.filter(
hospital => hospital.id === selectedHospital.value
).length
)
.map(doctorOption);
} else {
return data.doctors.map(doctorOption);
}
};
const reset = () => {
setSelectedDoctor(null);
setSelectedHospital(null);
};
return (
<div className="App">
<h3>React-Select multi select sample</h3>
<Select
id="hospital"
value={selectedHospital}
onChange={setSelectedHospital}
options={hospitalOptions()}
selectedDoctor={selectedDoctor}
/>
<Select
id="doctor"
value={selectedDoctor}
options={doctorOptions()}
onChange={setSelectedDoctor}
selectedHospital={selectedHospital}
/>
<pre selectedDoctor={selectedDoctor} selectedHospital={selectedHospital}>
Selected Doctor: {JSON.stringify(selectedDoctor || {}, null, 2)}
<br />
Available Doctors: {JSON.stringify(doctorOptions() || {}, null, 2)}
</pre>
<pre selectedDoctor={selectedDoctor} selectedHospital={selectedHospital}>
Selected Hospital: {JSON.stringify(selectedHospital || {}, null, 2)}
<br />
Available Hospitals: {JSON.stringify(hospitalOptions() || {}, null, 2)}
</pre>
<button onClick={reset}>Reset</button>
</div>
);
}
render(<App />, document.getElementById("root"));

Related

Filtering two different arrays and one of it is nested

when user come to my website, I want him to choose group by clicking proper button, then filter all products by checking if in groups products_bound is the matching item. It's not working... I think I missed something in showProduct function, Im new and still learning please tell me if I'm doing something in bad way or what could be better in my code.
My dummy state in file:
products = [
{
id: 1,
title: Apple,
price: 3.99,
},
{
id: 2,
title: Banana,
price: 5.99,
},
{
id: 3,
title: Carrot,
price: 1.99,
},
];
groups = [
{
name: Fruits,
products_bound: [
{
id: 1,
title: Apple,
price: 3.99,
},
{
id: 2,
title: Banana,
price: 5.99,
},
],
},
];
ChooseGroup.js
function ChooseGroup({ groups, showProductsButton }) {
return (
<div>
{groups.map((group) => {
return (
<button
id={group.name}
onClick={(e) => {
showProductsButton(e.target.id);
}}
>
{group.name}
</button>
);
})}
</div>
);
}
export default ChooseGroup;
index.js
function Homepage(props) {
const [filteredProductsByGroup, setFilteredProductsByGroup] = useState();
const [isThisGroupActive, setThisGroupActive] = useState(false);
function showProducts(clickedButtonId) {
const checkWhichGroupIsClicked = groups.filter(
(single) => single.name === clickedButtonId
);
const unpackGroup = checkWhichGroupIsClicked.map((single) =>
single.products_bound.map((i) => i)
);
const filter = products.filter((element) => {
unpackGroup.some((group) => {
return group == element.title;
});
});
return setFilteredProductsByGroup(filter), setThisGroupActive(true);
}
return (
<>
<Header />
<ChooseGroup groupList={props.groups} showProductsButton={showProducts} />
{isThisGroupActive ? (
filteredProductsByGroup.map((group) => {
return <Products groupDetails={group} />;
})
) : (
<div>Select group!</div>
)}
</>
);
}

calling setState from onClick JavaScript function not working

I am trying to create a button that will make visible a form to edit any contact on my list. However, when I press the button, nothing happens.
I have the initial state set to
this.state = {
contacts: [],
showEditWindow: false,
EditContactId: ''
};
I added a function:
editContact = (id) => {
this.setState({
showEditWindow: true, EditContactId: {id}
});
};
and a column:
{
title: "",
key: "action",
render: (record) => (
<button onClick={() => this.editContact(record.id)}
>
Edit
</button>
)
},
I imported EditContactModal and call it as
<EditContactModal reloadContacts={this.reloadContacts}
showEditWindow={this.state.showEditWindow}
EditContactId={this.state.EditContactId}/>
If I manually set this.state to showEditWindow:true, the window appears; however, either this.editContact(id) is not being called or it is not changing the state.
Calling this.deleteContact(id) works fine, as does setState in loadContacts() and reloadContacts()
What I am doing wrong?
Below are the full components.
Contacts.jsx
import { Table, message, Popconfirm } from "antd";
import React from "react";
import AddContactModal from "./AddContactModal";
import EditContactModal from "./EditContactModal";
class Contacts extends React.Component {
constructor(props) {
super(props);
this.state = {
contacts: [],
showEditWindow: false,
EditContactId: ''
};
this.editContact = this.editContact.bind(this);
};
columns = [
{
title: "First Name",
dataIndex: "firstname",
key: "firstname"
},
{
title: "Last Name",
dataIndex: "lastname",
key: "lastname"
},{
title: "Hebrew Name",
dataIndex: "hebrewname",
key: "hebrewname"
},{
title: "Kohen / Levi / Yisroel",
dataIndex: "kohenleviyisroel",
key: "kohenleviyisroel"
},{
title: "Frequent",
dataIndex: "frequent",
key: "frequent",
},{
title: "Do Not Bill",
dataIndex: "donotbill",
key: "donotbill"
},
{
title: "",
key: "action",
render: (record) => (
<button onClick={() => this.editContact(record.id)}
>
Edit
</button>
)
},
{
title: "",
key: "action",
render: (_text, record) => (
<Popconfirm
title="Are you sure you want to delete this contact?"
onConfirm={() => this.deleteContact(record.id)}
okText="Yes"
cancelText="No"
>
<a type="danger">
Delete{" "}
</a>
</Popconfirm>
),
},
];
componentDidMount = () => {
this.loadContacts();
}
loadContacts = () => {
const url = "http://localhost:3000/contacts";
fetch(url)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Network error.");
})
.then((data) => {
data.forEach((contact) => {
const newEl = {
key: contact.id,
id: contact.id,
firstname: contact.firstname,
lastname: contact.lastname,
hebrewname: contact.hebrewname,
kohenleviyisroel: contact.kohenleviyisroel,
frequent: contact.frequent.toString(),
donotbill: contact.donotbill.toString(),
};
this.setState((prevState) => ({
contacts: [...prevState.contacts, newEl],
}));
});
})
.catch((err) => message.error("Error: " + err));
};
reloadContacts = () => {
this.setState({ contacts: [] });
this.loadContacts();
};
deleteContact = (id) => {
const url = `http://localhost:3000/contacts/${id}`;
fetch(url, {
method: "delete",
})
.then((data) => {
if (data.ok) {
this.reloadContacts();
return data.json();
}
throw new Error("Network error.");
})
.catch((err) => message.error("Error: " + err));
};
editContact = (id) => {
this.setState({
showEditWindow: true, EditContactId: {id}
});
};
render = () => {
return (
<>
<Table
className="table-striped-rows"
dataSource={this.state.contacts}
columns={this.columns}
pagination={{ pageSize: this.pageSize }}
/>
<AddContactModal reloadContacts={this.reloadContacts} />
<EditContactModal reloadContacts={this.reloadContacts}
showEditWindow={this.state.showEditWindow}
EditContactId={this.state.EditContactId}/>
</>
);
}
}
export default Contacts;
EditContactModal.jsx
import { Button, Form, Input, Modal, Select } from "antd";
import React from "react";
import ContactForm from './ContactForm';
const { Option } = Select;
class EditContactModal extends React.Component {
formRef = React.createRef();
state = {
visible: this.props.showEditWindow,
};
onFinish = (values) => {
const url = `http://localhost:3000/contacts/${this.props.EditContactId}`;
fetch(url, {
method: "put",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
})
.then((data) => {
if(data.ok) {
this.handleCancel();
return data.json();
}
throw new Error("Network error.");
})
.then(() => {
this.props.reloadContacts();
})
.catch((err) => console.error("Error: " + err))
};
showModal = () => {
this.setState({
visible: true,
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
render() {
return (
<>
{/*<Button type="primary" onClick={this.showModal}>
Create New +
</Button>*/}
<Modal
title="Edit Contact"
visible={this.state.visible}
onCancel={this.handleCancel}
footer={null}
>
<ContactForm />
</Modal>
</>
);
}
}
export default EditContactModal;
if your aim is to perform an update to the state object, you must not pass mutable data, but copy it instead into a new object.
this will allow the state changes to be picked up.
so, prefer setState({ ...state, ...someObject }) over setState(someObject).

React: Nested Array Form - Input field onChange handler

The form data is set by an the data object. Need help figuring out how to update /handleChange the text inputs
I've tried unique name, those probably won't work because it wont match the "key" in the object.
Any help / input is appreciated!
Data:
export default
{
name: "Restaurants Name",
menu: [
{
category: "Appetizers",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Food 2",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Food 1",
desc: "",
price: "300"
}
]
},
{
category: "Entree",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Entree 1",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Entree 1",
desc: "",
price: "300"
}
]
},
]
}
Code:
import React, { useEffect, useState } from "react";
import "./../App.css"
import MenuData from "../data"
function Edit() {
const [formState, setFormState] = useState(MenuData);
useEffect(() => {
console.log(formState)
}, []);
const handleNameChange = (event) => {
const name = event.target.name;
// console.log(name)
setFormState(prevState => ({
formState: { // object that we want to update
...prevState.formState, // keep all other key-value pairs
[name]: event.target.value, // update the value of specific key
menu: {
...prevState.menu,
items: {
...prevState.menu.items
}
}
}
}))
// setFormState({
// ...formState,
// [name]: event.target.value,
// })
};
const handleChange = (categoryIndex, event) => {
// const values = [...formState]
// values[categoryIndex][event.target.name] = event.target.value;
// setFormState(values);
const name = event.target.name;
// setFormState(prevState => ({
// formState: {
// ...prevState.formState,
// menu: {
// ...prevState.menu,
// items{
// ...prevState.items
// }
// }
// }
// }));
};
return (
<div className="App">
<div>
<input name="nameField" id="nameField" maxLength="300" value={formState.name} onChange={handleNameChange} /> <br />
{formState.menu && formState.menu.map((menuitem, categoryIndex) => {
return (
<div key={categoryIndex}>
<div class="line"></div>
<h2>{menuitem.category}</h2>
<input name={"category-" + categoryIndex} id="CategoryField" maxLength="40" categoryIndex={categoryIndex} onChange={event => handleChange(categoryIndex, event)} value={menuitem.category} />
{
menuitem.items.map((item, index) => {
return(
<div key={index}>
<input name={"title-" + index + categoryIndex} id="titleField" maxLength="40" categoryIndex={categoryIndex} onChange={handleChange} value={item.title} /> <br />
<input name="desc" id="descField" maxLength="200" categoryIndex={categoryIndex} onChange={handleChange} value={item.desc} />
<br /><br />
</div>
)
})
}
</div>
)
}
)
}
</div>
</div>
);
}
export default Edit;
UPDATED
Not able to figure out the onChange function to updated nested items

Undefined values React

I'm trying to achieve making a suspend user button via updating the values of the user the status to Suspended, but the problem is the status is defined but other values are undefined did I do something wrong or is there any way to update the values to make the other variable like a name not required?
This is what I mean:
This is my code:
const User = (props) => (
<>
<DropdownButton id="dropdown-basic-button" title="Action">
<Dropdown.Item>
<a
href="user"
onClick={() => {
props.onSubmit(props.user[0]);
}}
>
<i className="fas fa-trash"></i> Suspend
</a>
</Dropdown.Item>
</DropdownButton>
</>
);
export default class Users extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.state = { users: [] };
}
componentDidMount() {
axios
.get("http://localhost:5000/users/")
.then((response) => {
this.setState({ users: response.data });
})
.catch((error) => {
console.log(error);
});
}
onSubmit(id) {
const user = {
name: this.state.name,
password: this.state.password,
email: this.state.email,
storeName: this.state.storeName,
storeUrl: this.state.storeUrl,
date: this.state.date,
status: "Suspended",
};
console.log(user);
axios
.post("http://localhost:5000/users/update/" + id, user)
.then((res) => console.log(res.data));
}
userList(currentuser) {
return (
<User
user={currentuser}
key={currentuser[0]}
onSubmit={this.onSubmit}
/>
);
}
render() {
const columns = [
{
name: "_id",
options: {
display: false,
},
},
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
return <>{this.userList(tableMeta.rowData)}</>;
},
},
},
];
const { users } = this.state;
return (
<>
<MUIDataTable data={users} columns={columns} />
</>
);
}
}
You didn't define nor set the User's individual attributes' values in the state! So, no wonder they show up as undefined, when you try to read them...
The simplest solution would be:
onSubmit(id) {
//let user = this.state.users.find(user => user.id === id); // find by id
let user = this.state.users[id]; // find by index
if (user) {
user.status = 'Suspended';
console.log(user);
axios
.post("http://localhost:5000/users/update/" + id, user)
.then((res) => console.log(res.data));
}
}

Error select based on selection from 1st select in React Hooks and dinamically inputs

I have two selects and I want to populate the second select base in the selection of the first one in react. When I select a countrie I want a select2 be displayed with its states and the value on the second select be updated with the value chose.
I have the following code,
const MyForm = (props) => {
const COUNTRIES = [
{
displayValue: "Country1",
value: "C1"
},
{
displayValue: "Country2",
value: "C2"
}
]
const STATES = {
"": [ {
displayValue: "",
value: ""
}],
"C1": [{
displayValue: "State 1",
value: "S11"
},
{
displayValue: "State 2",
value: "S12"
}],
"C2": [{
displayValue: "State n1",
value: "C21"
},
{
displayValue: "STate n2",
value: "C22"
}]
}
let inputsForms = {
country: {
elementType: 'select',
elementConfig: {
type: 'select',
placeholder: '',
options: COUNTRIES,
firstOption: "-- Choose Country"
},
value: ''
},
states: {
elementType: 'select',
elementConfig: {
type: 'select',
placeholder: '',
options: [], // I need these options depend on the countrie selected STATES["C1"]
firstOption: "-- Choose States"
},
value: ''
}
}
const [myForm, setmyForm] = useState(inputsForms);
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
const inputChangedHandler = (e, controlName) => {
const countrieValue = controlName ==="country"?e.target.value:"";
const stateOptions = myForm["states"].elementConfig;
stateOptions["options"] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value
})
});
setmyForm(updatedControls);
}
const ElementsArray = [];
for (let key in myForm) {
ElementsArray.push({
id: key,
config: myForm[key]
});
}
let form = (
<form>
{ElementsArray.map(el => (
<Input
key={el.id}
elementType={el.config.elementType}
elementConfig={el.config.elementConfig}
value={el.config.value}
changed={e => inputChangedHandler(e, el.id)}
firstOption={el.config.firstOption}
/>
))}
</form>
);
return(
<div>
{form}
</div>
);
}
export default MyForm;
The options charge on the select2, however when I select an option on the second select, the options dissappear and the value of the select2 is not updated.
Thanks.
As inputChangeHandler is getting called every input change, the data is resetting even there is change in the state. You could check for the contrieValue and set the state data so the data is not reset.
const inputChangedHandler = (e, controlName) => {
const countrieValue = controlName === "country" ? e.target.value : "";
if (countrieValue) {
const stateOptions = myForm["states"].elementConfig;
stateOptions["options"] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value
})
});
setmyForm(updatedControls);
}
}
You need to add a kind of conditional to update the state whenever the state is selected so that it does not affect the original country object.
const inputChangedHandler = (e, controlName) => {
if (countrolName === 'states') {
// your logic here
return;
}
const countrieValue = controlName === 'country' ? e.target.value : '';
const stateOptions = myForm['states'].elementConfig;
stateOptions['options'] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value,
}),
});
setmyForm(updatedControls);
};

Resources