I'm trying to delete a user dependents by dependents id using react-confirm-alert dialog but the list refreshes, how do I stop this from happening?
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
//Api data sample
"Details": [
{
"name": "test test",
"gender" "M"
"dependents": [
{
"blood_group": "A+",
"date_of_birth": "1990-08-10",
"gender": "Female",
"genotype": "AS",
"id": "621f191dcd7fe69a6a3b7",
}
],
},
]
function App() {
const [formalDetails, setFormalDetails] = useState([]);
//get formal details API call
const handleDelete = (detail) => {
const params = JSON.stringify({
"principal enrid": detail.principals_enrid,
"dependent id": detail.id,
mine: true,
});
Axios({
method: "POST",
url: "api",
headers: {
"Content-Type": "application/json",
},
data: params,
})
.then((response) => {
console.log(response.status);
//below is where my proble lies
setFormalDetails((current) =>
current.filter((dep) => {
return dep?.dependents?.id !== detail?.dependents?.id;
})
);
})
.catch(function (error) {
console.log(error);
});
};
const submit = (user) => {
confirmAlert({
title: 'Confirm to delete Dependent',
message: `Are you sure you want to delete ${user?.name}?`,
buttons: [
{
label: 'Yes',
onClick: () => handleRemove(user)
},
{
label: 'No',
onClick: () => null
}
]
});
}
return (
<div className="app">
{formalDetails.length === 0 ? (<p>No Data</p>) : (
formalDetails?.map((record, idx) => {
return (
<div key={idx}>
<p >{record.name}</p>
<p >{record.gender}</p>
{
record?.dependents?.map((user, indx) => {
return (
<div key={indx}>
<p >{user.name}</P>
<button
onClick={() => submit(user)}
type="button">
Delete
</button
</div
)
}}
</div>
)
)}
</div>
);
}
export default App;
Please how do can I delete a dependent by ID without refreshing the list/page/window to keep the user scrolling down to take more action(s) even after performing a delete action.
Related
My data is not shown when the app launches. It only shows when I tried to inspect the page.
I am reading JSON data that make take some time to be available. So, I added a async/await.
How do I fix my code so it displays on load?
Here is a snippet of my code:
const WeatherWidget = ({ id, editMode }) => {
const [roles, setRoles] = useState();
const getGoalData = async () => {
return (
[
{
"username": "user1",
"goal": "$5,000,200"
},
{
"username": "user2",
"goal": "$5,000,200"
},
{
"username": "user3",
"goal": "$4,000,199"
},
]
);
}
useEffect(() => {
const setDataRole = async () => {
var json = await getGoalData();
setRoles(json)
}
setDataRole();
}, [])
return (
<Container>
<div>
ticker from widget config
</div>
<StyledUl>
<Ticker>
{({ index }) => (
<>
{roles && roles.map(({ username, goal }, i) => (
<>
{i === 0 ? null : ','}
<StyledSpanName>
<span className="name">{username}</span>
</StyledSpanName>
<StyledSpanGoal> <span className="goal">{goal}</span></StyledSpanGoal>
</>
))
}
</>
)}
</Ticker>
</StyledUl>
</Container>
);
};
You don't need to load anything. The data is right there in the code. Just put it in roles right from the start.
In fact, you don't even need to use useState, since you never mutate the roles state. It could (and probably should) be a constant.
const WeatherWidget = ({ id, editMode }) => {
const [roles, setRoles] = useState(
[
{
"username": "user1",
"goal": "$5,000,200"
},
{
"username": "user2",
"goal": "$5,000,200"
},
{
"username": "user3",
"goal": "$4,000,199"
},
]
);
return (
<Container>
<div>
ticker from widget config
</div>
<StyledUl>
<Ticker>
{({ index }) => (
<>
{roles && roles.map(({ username, goal }, i) => (
<>
{i === 0 ? null : ','}
<StyledSpanName>
<span className="name">{username}</span>
</StyledSpanName>
<StyledSpanGoal> <span className="goal">{goal}</span></StyledSpanGoal>
</>
))
}
</>
)}
</Ticker>
</StyledUl>
</Container>
);
};
Seems like you forgot async:
useEffect(() => {
const setDataRole = async () => { // here
var res = await getGoalData()
var data = await res.json()
setRoles(data)
}
setDataRole();
}, [])
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).
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));
}
}
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"));
I'm using react table (https://github.com/react-tools/react-table) to render a table of expenses. In one column, there should be a button to 'approve' the expense. This is handled like so:
const columns = [
{
Header: "Description",
accessor: "description"
},
{
Header: "Approve",
accessor: d => {
return <button onClick={this.approveExpense(d.id)}>Approve</button>;
},
id: "approved"
}
];
Where the approveExpense function is defined as:
approveExpense = id => {
fetch(`${apiRoot}expenses_pending/`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Token ${this.props.auth.token}`
},
body: JSON.stringify({
id: id
})
}).then(res => {
if (res.status === 200) {
this.setState({
issues: this.state.expenses.filter(expense => expense.id != id)
});
} else {
console.log("Error");
}
});
};
Strangely, however, when the page loads, it behaves as if all of these buttons are being repeatedly pressed, many times per second (until the fans start going crazy and I stop the react server).
Am I doing something stupid?
Full class:
class ExpensePendingAdmin extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
fetch(`${apiRoot}expenses_pending`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Token ${this.props.auth.token}`
}
})
.then(response => response.json())
.then(data => {
console.log(data);
this.setState({
expenses: data
});
});
}
approveExpense = id => {
fetch(`${apiRoot}expenses_pending/`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Token ${this.props.auth.token}`
},
body: JSON.stringify({
id: id
})
}).then(res => {
if (res.status === 200) {
this.setState({
issues: this.state.expenses.filter(expense => expense.id != id)
});
} else {
console.log("Error");
}
});
};
render() {
const columns = [
{
Header: "Description",
accessor: "description"
},
{
Header: "Logged At",
id: "loggedAt",
accessor: d =>
moment(d.expense_incur_datetime).format("HH:mm - ddd d/M/YYYY")
},
{
Header: "Amount",
accessor: d => `£${d.amount}`,
id: "amount"
},
{
Header: "Approve",
accessor: d => {
return <button onClick={this.approveExpense(d.id)}>Approve</button>;
},
id: "approved"
},
{
Header: "Paid",
accessor: d => {
console.log(d);
return d.is_unpaid ? "No" : "Yes";
},
id: "paid"
}
];
return (
<div className="container-fluid">
{this.state.expenses ? (
<>
<div className="row">
<div className="col text-center">
<h2>Pending Expenses</h2>
</div>
</div>
<div className="row">
<div className="col">
<ReactTable
data={this.state.expenses}
columns={columns}
minRows="0"
minWidth="50"
showPagination={false}
/>
</div>
</div>
</>
) : (
"LOADING"
)}
</div>
);
}
}
Methods in event handlers in JSX do not require parentheses, if you want to pass down a parameter simply wrap it in a function:
onClick={() => this.approveExpense(d.id)}
All other answers are right, however you could also improve the syntax of your function calling by making your function will multiple parameter sets :
approveExpense = id => ev => {
And then setting your accessor rendering like this :
accessor: d => <button onClick={this.approveExpense(d.id)}>Approve</button>;
The function : this.approveExpense(d.id) will return another function capable of receiving another parameter (here, the click event names ev) and will work like a charm
You need to pass the approveExpense() function as a callback function like, so it will only trigger when you click.
<button onClick={(d) => this.approveExpense(d.id)}>Approve</button>
The problem with your code is that you are passing the event handler in the wrong way:
return <button onClick={this.approveExpense(d.id)}>Approve</button>;
by using directly this.approveExpense(d.id) inside your JSX code you are telling javascript to execute that function as soon as the interpreter reads it.
Instead you should proxy the function execution on the click, like this:
return <button onClick={(e) => {this.approveExpense(d.id)}}>Approve</button>;
For more in depth explanation on how to pass function to components in React you can check https://reactjs.org/docs/faq-functions.html