Key prop not being recognized in React - reactjs

I have a component called ListSuppliers, in it I have a bootstrap modal, it contains a table, and I want to populate that table from a postgresql table. Below is what I have in my Fragment section for the modal:
<div className="modal fade" id="myModal">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header d-flex justify-content-center">
<h4 className="modal-title">Supplier Name</h4>
</div>
<div className="modal-body">
<h3 className="text-center mt-2">Search Bar</h3>
<form>
<input
type="text"
className="form-control"
/>
</form>
<table className="table mt-5">
<thead>
<tr>
<th>Supplier Name</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{suppliers.map(supplier => (
<tr key={supplier.supplier_id}>
<td>{supplier.supplier_name}</td>
<td><button className="btn btn-danger" onClick={()=> deleteSupplier(supplier.supplier_id)}>Delete</button></td>
</tr>
))}
</tbody>
</table>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
And here is what I have for my ListSuppliers components and routes:
const ListSuppliers = () => {
const [suppliers, setSuppliers] = useState([]);
//Populate modal table function
const getSuppliers = async () => {
try {
const response = await fetch("http://localhost:5000/supplier");
const jsonData = await response.json();
setSuppliers(jsonData);
} catch (err) {
console.log(err.message);
}
}
//routes from index.js file
app.get("/supplier", async (req, res) => {
try {
const allSuppliers = await pool.query("SELECT supplier_name FROM supplier ORDER BY supplier_id ASC");
res.json(allSuppliers.rows);
} catch (err) {
console.error(err.message);
}
})
//Delete supplier entry
app.delete("/supplier/:id", async (req, res) => {
try {
const {id} = req.params;
const deleteSupplier = await pool.query('DELETE FROM supplier WHERE supplier_id = $1', [id]);
res.json(id);
} catch (err) {
console.error(err.message);
}
})
My modal displays the information as intended, however I'm getting this error:
Warning: Each child in a list should have a unique "key" prop
Check the render method of `ListSuppliers`
I don't understand why I'm getting the error as I included the key prop in table row:
{suppliers.map(supplier => (
<tr key={supplier.supplier_id}>
<td>{supplier.supplier_name}</td>
<td><button className="btn btn-danger" onClick={()=> deleteSupplier(supplier.supplier_id)}>Delete</button></td>
</tr>
))}
Any help is much appreciated

Check if supplier_id is present.
Lastly, try this and see if the warning comes up
{suppliers.map((supplier, index) => (
<tr key={index}>
<td>{supplier.supplier_name}</td>
<td><button className="btn btn-danger" onClick={()=> deleteSupplier(supplier.supplier_id)}>Delete</button></td>
</tr>
))}

Related

React JS cannot Update the record

i have create the simple crud system in React with Laravel. i shall be able to add the records and view the records but couldn't update the records. what i tried so far i attached below. i attached the full code for easy for understanding. i think the problem is occur in the update url is there any error. Api i tested through the console it is working well in update part
i attached the when i looked at the console
127.0.0.1:8000/api/update/undefined:1
Failed to load resource: the server responded with a status of 405 (Method Not Allowed)
Full code
import axios from 'axios';
import {useEffect, useState } from "react";
function EmployeeLoad()
{
const [id, setId] = useState('');
const [name, setName] = useState("");
const [address, setAddress] = useState("");
const [mobile, setMobile] = useState("");
const [users, setUsers] = useState([]);
useEffect(()=>
{
Load();
},[])
async function Load()
{
const result = await axios.get(
"http://127.0.0.1:8000/api/employees");
setUsers(result.data);
console.log(result.data);
}
async function save(event)
{
event.preventDefault();
try
{
await axios.post("http://127.0.0.1:8000/api/save",
{
name: name,
address: address,
mobile: mobile
});
alert("Employee Registation Successfully");
setId("");
setName("");
setAddress("");
setMobile("");
}
catch(err)
{
alert("User Registation Failed");
}
}
async function editEmployee(users)
{
setName(users.name);
setAddress(users.address);
setMobile(users.mobile);
setId(users.id);
}
async function update(event)
{
event.preventDefault();
try
{
await axios.patch("http://127.0.0.1:8000/api/update/"+ users.id,
{
id: id,
name: name,
address: address,
mobile: mobile
});
alert("Employee Registation Successfully");
setId("");
setName("");
setAddress("");
setMobile("");
}
catch(err)
{
alert("User Registation Failed");
}
}
return (
<div>
<h1>Employee Details</h1>
<div class="container mt-4" >
<form>
<div class="form-group">
<input type="text" class="form-control" id="employee_id"
value={id}
onChange={(event) =>
{
setId(event.target.value);
}}
/>
<label>employeeName</label>
<input type="text" class="form-control" id="employeeName"
value={name}
onChange={(event) =>
{
setName(event.target.value);
}}
/>
</div>
<div class="form-group">
<label>employeeAddress</label>
<input type="text" class="form-control" id="employeeAddress"
value={address}
onChange={(event) =>
{
setAddress(event.target.value);
}}
/>
</div>
<div class="form-group">
<label>Mobile</label>
<input type="text" class="form-control" id="employeeMobile"
value={mobile}
onChange={(event) =>
{
setMobile(event.target.value);
}}
/>
</div>
<div>
<button class="btn btn-primary mt-4" onClick={save}>Register</button>
<button class="btn btn-primary mt-4" onClick={update}>Update</button>
</div>
</form>
</div>
<table class="table table-dark" align="center">
<thead>
<tr>
<th scope="col">Employee Id</th>
<th scope="col">Employee Name</th>
<th scope="col">Employee Address</th>
<th scope="col">Employee Mobile</th>
<th scope="col">Option</th>
</tr>
</thead>
{users.map(function fn(item)
{
return(
<tbody>
<tr>
<th scope="row">{item.id} </th>
<td>{item.name}</td>
<td>{item.address}</td>
<td>{item.mobile}</td>
<td>
<button type="button" class="btn btn-warning" onClick={() => editEmployee(item)} >Edit</button>)}
<button type="button" class="btn btn-dark" >Delete </button>
</td>
</tr>
</tbody>
);
})}
</table>
</div>
);
}
export default EmployeeLoad;
while Running the code this error displayed
useEffect(() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
There's no users.id and that's why the dynamically-constructed path is wrong and the API rejects it. http://127.0.0.1:8000/api/update/undefined
The error is thrown because of this line:
await axios.patch("http://127.0.0.1:8000/api/update/"+ users.id,
{
id: id,
name: name,
address: address,
mobile: mobile
});
You can try to await the call to the Load function in the useEffect.
Also, try adding if (!users) {return null;}
before the main JSX return.
I think there's something wrong with what your API returns.
Check what the users variable contains.
Something like:
import axios from 'axios';
import {useEffect, useState } from "react";
function EmployeeLoad()
{
const [id, setId] = useState('');
const [name, setName] = useState("");
const [address, setAddress] = useState("");
const [mobile, setMobile] = useState("");
const [users, setUsers] = useState([]);
useEffect(async ()=>
{
await Load();
},[])
async function Load()
{
const result = await axios.get(
"http://127.0.0.1:8000/api/employees");
setUsers(result.data);
console.log(result.data);
}
async function save(event)
{
event.preventDefault();
try
{
await axios.post("http://127.0.0.1:8000/api/save",
{
name: name,
address: address,
mobile: mobile
});
alert("Employee Registation Successfully");
setId("");
setName("");
setAddress("");
setMobile("");
}
catch(err)
{
alert("User Registation Failed");
}
}
async function editEmployee(users)
{
setName(users.name);
setAddress(users.address);
setMobile(users.mobile);
setId(users.id);
}
async function update(event)
{
event.preventDefault();
try
{
console.log('Let Aifos Si Prahs know what is here', users);
await axios.patch("http://127.0.0.1:8000/api/update/"+ users.find(u => u.id === id).id || id,
{
id: id,
name: name,
address: address,
mobile: mobile
});
alert("Employee Registation Successfully");
setId("");
setName("");
setAddress("");
setMobile("");
}
catch(err)
{
alert("User Registation Failed");
}
}
if (users.length <= 0) return null;
return (
<div>
<h1>Employee Details</h1>
<div class="container mt-4" >
<form>
<div class="form-group">
<input type="text" class="form-control" id="employee_id"
value={id}
onChange={(event) =>
{
setId(event.target.value);
}}
/>
<label>employeeName</label>
<input type="text" class="form-control" id="employeeName"
value={name}
onChange={(event) =>
{
setName(event.target.value);
}}
/>
</div>
<div class="form-group">
<label>employeeAddress</label>
<input type="text" class="form-control" id="employeeAddress"
value={address}
onChange={(event) =>
{
setAddress(event.target.value);
}}
/>
</div>
<div class="form-group">
<label>Mobile</label>
<input type="text" class="form-control" id="employeeMobile"
value={mobile}
onChange={(event) =>
{
setMobile(event.target.value);
}}
/>
</div>
<div>
<button class="btn btn-primary mt-4" onClick={save}>Register</button>
<button class="btn btn-primary mt-4" onClick={update}>Update</button>
</div>
</form>
</div>
<table class="table table-dark" align="center">
<thead>
<tr>
<th scope="col">Employee Id</th>
<th scope="col">Employee Name</th>
<th scope="col">Employee Address</th>
<th scope="col">Employee Mobile</th>
<th scope="col">Option</th>
</tr>
</thead>
{users.map(function fn(item)
{
return(
<tbody>
<tr>
<th scope="row">{item.id} </th>
<td>{item.name}</td>
<td>{item.address}</td>
<td>{item.mobile}</td>
<td>
<button type="button" class="btn btn-warning" onClick={() => editEmployee(item)} >Edit</button>)}
<button type="button" class="btn btn-dark" >Delete </button>
</td>
</tr>
</tbody>
);
})}
</table>
</div>
);
}
export default EmployeeLoad;
Althoug this won't help if your users object doesn't contain an id.

Add and Edit do at one button React

I am beginner of react. I want to add and edit both operations at one button.
but it is not working. what i tried so far is the attached code along with the screenshot image.
I will able to add the records and able to view the records when i click the table row particular row record will be passing to form successfully. But I want edit and add the record of same button. i create the method save inside method I called save and edit functions
screenshot
this is the function i wrote the below
async function save(users)
import axios from 'axios';
import {useEffect, useState } from "react";
function EmployeeLoad()
{
const [name, setName] = useState("");
const [address, setAddress] = useState("");
const [mobile, setMobile] = useState("");
var currentEmployeeID = "";
const [users, setUsers] = useState([]);
useEffect(()=>
{
Load();
},[])
async function editEmployee(users)
{
setName(users.name);
setAddress(users.address);
setMobile(users.mobile);
currentEmployeeID = users.id;
console.log(users.id);
}
async function Load()
{
const result = await axios.get(
"http://127.0.0.1:8000/api/employees");
setUsers(result.data);
console.log(result.data);
}
async function save(users)
{
if(users.id == '')
{
await axios.post("http://127.0.0.1:8000/api/save",
{
name: name,
address: address,
mobile: mobile
}
);
alert("Employee Registation success");
}
else
{
alert("edit");
}
}
return (
<div>
<h1>Employee Details</h1>
<div class="container mt-4" >
<form>
<div class="form-group">
<label>employeeName</label>
<input type="text" class="form-control" id="employeeName"
value={name}
onChange={(event) =>
{
setName(event.target.value);
}}
/>
</div>
<div class="form-group">
<label>employeeAddress</label>
<input type="text" class="form-control" id="employeeAddress"
value={address}
onChange={(event) =>
{
setAddress(event.target.value);
}}
/>
</div>
<div class="form-group">
<label>Mobile</label>
<input type="text" class="form-control" id="employeeMobile"
value={mobile}
onChange={(event) =>
{
setMobile(event.target.value);
}}
/>
</div>
<button class="btn btn-primary mt-4" onClick={save}>Register</button>
</form>
</div>
<table class="table table-dark" align="center">
<thead>
<tr>
<th scope="col">Employee Id</th>
<th scope="col">Employee Name</th>
<th scope="col">Employee Address</th>
<th scope="col">Employee Mobile</th>
<th scope="col">Option</th>
</tr>
</thead>
{users.map(function fn(item)
{
return(
<tbody>
<tr>
<th scope="row">{item.id} </th>
<td>{item.name}</td>
<td>{item.address}</td>
<td>{item.mobile}</td>
<td>
<button type="button" class="btn btn-warning" onClick={() => editEmployee(item)} >Edit</button>
<button type="button" class="btn btn-dark" >Delete </button>
</td>
</tr>
</tbody>
);
})}
</table>
</div>
);
}
export default EmployeeLoad;
Inside your table use the currentEmployeeID and render a save button if you are in edit mode
{currentEmployeeID === item.id ? (
<button type="button" class="btn btn-warning" onClick={save} >Save</button> : (
<button type="button" class="btn btn-warning" onClick={() => editEmployee(item)} >Edit</button>
)}
So if this specific row is in edit mode, by pressing the button, it will save the entry, or else it will show "Edit". Dont forget to reset the currentEmployeeID when the save process completes.

Message prints in every Dynamic Accordion in ReactJs

I have a dynamic Accordion in ReactJs. I am getting the message from my backend. but it's printing in every Accordion. I'm sharing the code
import React, { useState, useEffect } from "react";
import ApplicantDash from "./ApplicantDash";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
} from "#material-ui/core";
import * as FcIcons from "react-icons/fc";
import ApplicantService from "../services/ApplicantService";
export default function AvailJobs() {
const [aplcntEmail, setAplcntEmail] = useState("aman#gmail.com"); //change to aplcntemail
const [isShow, setIsShow] = useState(false);
const [msg, setMsg] = useState([""]);
const [job, setJob] = useState([
{
jobTitle: "",
dateOfPosting: Date,
lastDateToApply: new Date().toLocaleDateString([], {
year: "numeric",
month: "long",
day: "numeric",
}),
preferableSkills: [],
requiredExp: 0,
recruiterEmail: "",
companyName: "",
companyAddress: "",
},
]);
useEffect(() => {
const data = ApplicantService.getAllJobs()
.then((response) => {
console.log(response.data);
setJob(response.data);
})
.catch((error) => {
alert(error.response.data);
});
}, []);
const onApplyButton = (item,key) => {
const data2 = ApplicantService.applyForJob(aplcntEmail, item)
.then((response) => {
console.log(response.data);
setIsShow(true);
setMsg(response.data)
})
.catch((error) => {
setIsShow(true);
setMsg(error.response.data);
});
};
return (
<div>
<ApplicantDash />
<div className="container bg-light">
<div className="card-bodies">
<section className="mb-4">
<h2 className="h1-responsive font-weight-bold text-center my-4">
All Available jobs
</h2>
</section>
{job.map((item, key) => (
<>
<Accordion key={key}>
<AccordionSummary
expandIcon={<FcIcons.FcExpand />}
aria-controls="panel1a-content"
id="panel1a-header"
className="Accordian"
>
<Typography>
<div className="d-flex p-1 justify-content-evenly">
<div className="p-1">
<b> Job: </b> {item.jobTitle}
</div>
<div className="p-2"></div>
<div className="p-1">
<b> Company: </b> {item.companyName}
</div>
<div className="p-2"></div>
<div className="p-1">
<b> Last Date: </b> {item.lastDateToApply}
</div>
</div>
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<div className="container">
<table class="table table-borderless">
<tbody>
<tr>
<td>JOB TITLE</td>
<td>:</td>
<td>
<b>{item.jobTitle}</b>
</td>
</tr>
<tr>
<td>Company</td>
<td>:</td>
<td>
<b>{item.companyName}</b>
</td>
</tr>
<tr>
<td>Address</td>
<td>:</td>
<td>
<b>{item.companyAddress}</b>
</td>
</tr>
<tr>
<td>Last Date to Apply</td>
<td>:</td>
<td>
<b>{item.lastDateToApply}</b>
</td>
</tr>
<tr>
<td>Experience</td>
<td>:</td>
<td>
<b>{item.requiredExp}</b>
</td>
</tr>
<tr>
<td> Skills </td>
<td>:</td>
<td>
<table className="table table-condensed w-auto table-borderless table-hover">
{item.preferableSkills.map((S, index1) => {
return (
<tbody key={index1}>
<td scope="col">
{index1 + 1}.<b>{S}</b>
</td>
</tbody>
);
})}
</table>
</td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<button
type="button"
class="btn btn-primary"
onClick={() => onApplyButton(item,key)}
>
Apply for the job{" "}
</button>
</td>
</tr>
</tbody>
{isShow && <>
{msg}
</>}
</table>
</div>
</Typography>
</AccordionDetails>
</Accordion>
</>
))}
</div>
</div>
</div>
);
}
Now when I click on Apply for this job button. The message I get from backend prints only to Active accordion
Here some pictures which might help.
enter image description here
As you can see the response from backend is prints in the both of the accordion
Issue
The issue here is that you've a single boolean isShow state and a single msg state, and all the accordion detail sections use the same single isShow state to conditionally render the msg state.
Solution
A simple solution would be to store the id, or title, or index, of the accordion to show the message of.
Example:
export default function AvailJobs() {
...
const [isShow, setIsShow] = useState({}); // <-- initially empty object
...
const onApplyButton = (item, key) => {
ApplicantService.applyForJob(aplcntEmail, item)
.then((response) => {
console.log(response.data);
setMsg(response.data);
})
.catch((error) => {
setMsg(error.response.data);
})
.finally(() => {
setIsShow(show => ({
...show,
[key]: true // <-- set true the specific key
}));
});
};
return (
<div>
...
{job.map((item, key) => (
<Accordion key={key}>
...
<AccordionDetails>
<Typography>
<div className="container">
<table class="table table-borderless">
<tbody>
...
<tr>
...
<td>
<button
type="button"
class="btn btn-primary"
onClick={() => onApplyButton(item, key)}
>
Apply for the job
</button>
</td>
</tr>
</tbody>
{isShow[key] && <>{msg}</>} // <-- check if isShow[key] is truthy
</table>
</div>
</Typography>
</AccordionDetails>
</Accordion>
))}
...
</div>
);
}

edit and update table cell dynamically in react

I am new to react. I want to achieve the below functionality in react for a grading table of students. How can I do that?
Please refer to the images.
Change the table cell into an input box
Editable cells in the table
Here is the snippet I have tried till now
Improved your code. you can change as per your requirement. Live demo
const Grades = () => {
const [grade, setGrade] = useState("");
const [data, setData] = useState([]);
const [showEdit, setShoEdit] = useState(0);
var count = 0;
const handleChange = (event) => {
setGrade(event.target.value);
};
const addGrade = () => {
setData([...data, { Grade: grade, id: Math.floor(Math.random()*100) }]);
setGrade("");
};
const editGrade = (row) => {
console.log(row)
setShoEdit(row.id);
setGrade(row.id);
}
const saveGrade = (row) => {
let update = data.map(list =>
list.id === row.id ? ({...list, Grade: grade}) : list
);
setData([...update]);
}
return (
<div className="container-xl">
<div className="form-group row">
<div className="col-md-1">
<label className="col-form-label ">Grade</label>
</div>
<div className="col-md-4">
<input
type="text"
value={grade}
className="form-control"
name="Grade"
onChange={handleChange}
/>
</div>
{!showEdit && <div className="form-group col-md-6">
<button
type="button"
className="btn btn-outline-primary"
onClick={addGrade}
>
Add Grade
</button>
</div>}
</div>
<div className="form-group row">
<table className="table">
<thead className="thead-dark">
<tr>
<th>#</th>
<th>Grades</th>
<th>Grade Id</th>
<th>Operations</th>
</tr>
</thead>
<tbody>
{data.map((row,i) => (
<tr key={row.id}>
<td>{++count}</td>
<td>{row.Grade}</td>
<td>{row.id}</td>
<td>
{!(row.id === showEdit) ? <button
onClick={()=> editGrade(row)}
key={row.id}
className="btn btn-outline-primary"
type="button"
>
Edit
</button>:
<button
type="button"
className="btn btn-outline-primary"
onClick={()=>saveGrade(row)}
>
Save
</button>}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default Grades;

Why would is my function not executing onclick in pop up window using reactjs?

I am attempting to execute a function through an onclick event, however, nothing happens. My aim is to have the function firing off once the download button in the pop-up window is clicked. My aim is to have the downloadJobs event fire once the Download button is clicked.
Any advice to resolve this issue would be truly appreciated.
class LoadTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
Search: "Search",
visible: false,
sort: {
column: null,
direction: 'desc',
},
}
this.doSearch = this.doSearch.bind(this);
this.runLog = this.runLog.bind(this);
this.downloadOutput = this.downloadOutput.bind(this);
}
componentDidMount() {
this.props.getJobs()
.then((res) => {
this.setState({
data: res.results.response || [],
visible: false
})
});
}
doSearch(e) {
const { name, value } = e.target;
this.setState({
[name]: value
});
console.log("Initiate Search");
}
runLog() {
console.log("Initiate Run Log");
}
downloadOutput() {
var name = document.getElementById('logBody');
console.log("execute");
//const element = document.createElement("a");
//const file = new Blob([content], { type: 'text/plain' });
//element.href = URL.createObjectURL(file);
//element.download = "log.txt";
//document.body.appendChild(element); // Required for this to work in FireFox
//element.click();
}
render() {
const { data, Search, visible } = this.state;
return data.length > 0 ? (
<div className="row row-centered">
<div className="col-lg-12 col-md-12 col-sm-12 col-xs-12 col-centered">
<div id="Search" className="row col-xs-5 col-lg-2">
<div className="form-group">
<input className='form-control' type="text" placeholder="Search" name="Search" value={Search} onChange={this.doSearch} autoFocus />
</div>
</div>
<table className="table table-striped">
<thead>
<tr>
<th onClick={e => this.doSort('name')}>Name</th>
<th onClick={e => this.doSort('job')}>Job</th>
<th onClick={e => this.doSort('start')}>Start</th>
<th onClick={e => this.doSort('end')}>End</th>
<th onClick={e => this.doSort('status')}>Status</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{
data.map((dt) => {
return (
<tr key={dt.id}>
<td>{dt.name}</td>
<td>{dt.job}</td>
<td>{dt.start}</td>
<td>{dt.end}</td>
{ dt.status ?
<td>
<div className="alert alert-success" role="alert"></div>
</td>
:
<td>
<div className="alert alert-danger" role="alert"></div>
</td>
}
<td><button type="button" className="btn btn-primary" onClick={this.runLog}>Run Job</button></td>
<td><button type="button" className="btn btn-info" onClick={() => this.refs.modalLog.open()}>View Run Log</button>
<PureModal
header={dt.name}
scrollable
width="300px"
draggable
footer={<div><button type="button" className="btn btn-info" onClick={() => this.downloadOutput }>Download Job {dt.name}</button></div>}
onClose={this.HandleClose}
ref="modalLog"
>
<p id="logBody">{dt.logs}</p>
</PureModal>
</td>
</tr>
);
})
}
</tbody>
</table>
</div>
</div>
) :
<div className="row">
<div className="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>No Data to Display at the moment</p>
</div>
</div>;
}
}
function mapStateToProps(state) {
return {
};
}
const mapDispatchToProps = dispatch => ({
getJobs: () => dispatch(jobActions.getJobs())
});
export default connect(mapStateToProps, mapDispatchToProps)(LoadTable);
The way this onClick handler is set up right now is that it is invoking a call back function which returns you the downloadOutput function but this function itself is not being invoked since no () are present. You would need to rewrite it to be onClick={() => this.downloadOutput()}
However, since downloadOuput is not receiving any parameters, you don't have to have it invoke through a call back function, then the onClick event itself will be used to invoke this function directly. onClick={this.downloadOutput}
Also,
this.downloadOutput = this.downloadOutput.bind(this) within the constructor to bind the this value.
Hope that helped (:

Resources