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.
Related
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.
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>
);
}
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>
))}
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;
I am trying to target a specific row to edit on my table in React.
My code looks something like this...
const [rowData, setRowData] = useState({ kind: { str: '', row: '' }});
const onChange = e => {
setRowData({...rowData, [e.target.name]: e.target.value }}
}
arr.map((ele, index) => (
<tr>
<td><input type='text' name='kind' value={kind.str} row={index} onChange={e => onChange(e)}></td>
</tr>
))
I didn't write all my code but I think that should be sufficient enough for an answer.
Basically I press a button edit that makes the row editable. When I press this button actually all rows and columns become editable which is not what I want but regardless of this outcome - I go to update the table and only that one input gets updated, unfortunately I get this error
A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
After reading the error I then decided to change my onChange function to this...
const onChange = e => {
setRowData({...rowData, [e.target.name]: { str: e.target.value, row: e.target.getAttribute('row')}}
}
the above solves the issue from making my controlled component becoming uncontrolled however all the input fields on each row that match the kind name get updated which is not the desired functionality I want.
How can I solve this issue. Eventually what I want is an editable table that the user can update a single row and update the database with the data they input.
The arr variable I am using is actually named files.
I have another component which gets all my files from my database and sets it to the files variable
const [files, setFiles] = useState([]);
useEffect(() => {
const fetchFiles = async () => {
setLoading(true);
const res = await fetch('http://localhost:5000/api/files/all', {
method: 'GET',
});
const data = await res.json();
setFiles(data);
setLoading(false);
};
fetchFiles();
}, []);
I then pass in as props files to my Items component which is where the code that I wrote for my question lives in.
Most of the code is here...
ShowList component
const ShowList = () => {
const [files, setFiles] = useState([]);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [filesPerPage, setFilesPerPage] = useState(5);
const [yourUploads, setYourUploads] = useState(false);
useEffect(() => {
const fetchFiles = async () => {
setLoading(true);
const res = await fetch('http://localhost:5000/api/files/all', {
method: 'GET',
});
const data = await res.json();
setFiles(data);
setLoading(false);
};
fetchFiles();
}, []);
const indexOfLastPage = currentPage * filesPerPage;
const indexOfFirstPage = indexOfLastPage - filesPerPage;
const currentFiles = files.slice(indexOfFirstPage, indexOfLastPage);
const paginate = (pageNumber) => setCurrentPage(pageNumber);
return (
<Fragment>
<div className='container'>
<h3 className='text-center'>
{yourUploads ? 'Your uploads' : 'All uploads'}
</h3>
<div className='d-flex dropdown'>
<button
className='btn mb-3 mr-3'
type='button'
id='dropdownMenuButton'
data-toggle='dropdown'
aria-haspopup='true'
aria-expanded='false'
>
Pages per row {filesPerPage}
</button>
<div className='dropdown-menu' aria-labelledby='dropdownMenuButton'>
<button
className='dropdown-item'
onClick={() => setFilesPerPage(5)}
>
5
</button>
<button
className='dropdown-item'
onClick={() => setFilesPerPage(10)}
>
10
</button>
</div>
<div className='dropdown'>
<button
className='btn mb-3'
type='button'
id='dropdownUploadsButton'
data-toggle='dropdown'
aria-haspopup='true'
aria-expanded='false'
>
{yourUploads ? 'your uploads' : 'all uploads'}
</button>
<div
className='dropdown-menu'
aria-labelledby='dropdownUploadButton'
>
<button
className='dropdown-item'
onClick={() => setYourUploads(true)}
>
your uploads
</button>
<button
className='dropdown-item'
onClick={() => setYourUploads(false)}
>
all uploads
</button>
</div>
</div>
</div>
<table id='myTable' className='table table-striped w-100'>
<thead>
<tr>
<th scope='col'>
<small>Title</small>
</th>
<th scope='col'>
<small>Kind</small>
</th>
<th scope='col'>
<small>Size</small>
</th>
<th scope='col'>
<small>Strength</small>
</th>
<th scope='col'>
<small>Combinations</small>
</th>
<th scope='col'>
<small>Favors</small>
</th>
<th scope='col'>
<small>Stock</small>
</th>
<th scope='col'>
<small>Carousel</small>
</th>
<th scope='col'>
<small>Owner</small>
</th>
<th scope='col'>
<small>Edit</small>
</th>
<th scope='col'>
<small>Delete</small>
</th>
</tr>
</thead>
<Items
files={currentFiles}
loading={loading}
yourUploads={yourUploads}
/>
</table>
<Pagination
filesPerPage={filesPerPage}
totalFiles={files.length}
paginate={paginate}
/>
</div>
</Fragment>
);
};
Items component
const Items = ({ files, loading, yourUploads }) => {
const [myUploads, setMyUploads] = useState([]);
const [editable, setEditable] = useState(false);
const [rowData, setRowData] = useState({
kind: { str: '', row: '' },
});
const { kind } = rowData;
useEffect(() => {
const fetchMyUploads = async () => {
const res = await fetch('http://localhost:5000/api/files/all/mine', {
method: 'GET',
});
const data = await res.json();
setMyUploads(data);
};
fetchMyUploads();
}, [files]);
const onChange = (e) => {
setRowData({ ...rowData, [e.target.name]: e.target.value });
};
const onSubmit = async (e, file_id) => {
e.preventDefault();
if (!editable) {
setEditable(!editable);
} else {
await fetch(`http://localhost:5000/api/files/${file_id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(rowData),
});
setEditable(!editable);
}
};
if (loading) {
return (
<tbody>
<tr>
<td>loading...</td>
</tr>
</tbody>
);
}
const list = yourUploads
? myUploads.map((file) => (
<tr key={file._id}>
<td>
<small>{file.title}</small>
</td>
<td>
<small>{file.kind}</small>
</td>
<td>
<small>{file.size}</small>
</td>
<td>
<small>{file.strength}</small>
</td>
<td>
<small>{file.combinations}</small>
</td>
<td>
<small>{file.favors}</small>
</td>
<td>
<small
className={file.availability ? 'alert-success' : 'alert-danger'}
>
{file.availability ? 'in stock' : 'out of stock'}
</small>
</td>
<td>
<small>{file.isCarousel ? 'carousel' : 'not caorousel'}</small>
</td>
<td>
<small>{file.owner}</small>
</td>
<td>
<button className='btn btn-dark'>
<small>edit</small>
</button>
</td>
<td>
<button className='btn btn-danger'>
<small>delete</small>
</button>
</td>
</tr>
))
: files.map((file, index) => (
<tr key={file._id}>
<td>
<small>{file.title}</small>
</td>
<td>
<small>
{editable ? (
<input
type='text'
name='kind'
value={kind.str}
row={index}
onChange={(e) => onChange(e)}
/>
) : (
file.kind
)}
</small>
</td>
<td>
<small>{file.size}</small>
</td>
<td>
<small>{file.strength}</small>
</td>
<td>
<small>{file.combinations}</small>
</td>
<td>
<small>{file.favors}</small>
</td>
<td>
<small
className={file.availability ? 'alert-success' : 'alert-danger'}
>
{file.availability ? 'in stock' : 'out of stock'}
</small>
</td>
<td>
<small>{file.isCarousel ? 'carousel' : 'not carousel'}</small>
</td>
<td>
<small>{file.owner}</small>
</td>
<td>
<button
className='btn btn-dark'
onClick={(e) => onSubmit(e, file._id)}
>
<small>{editable ? 'save' : 'edit'}</small>
</button>
</td>
<td>
<button className='btn btn-danger'>
<small>delete</small>
</button>
</td>
</tr>
));
return <tbody>{list}</tbody>;
};
This error message usually comes up when you pass null or undefined to the input value prop. If value prop is empty, then react thinks it became uncontrolled component.
So you need to fix the error here.
arr.map((ele, index) => (
<tr>
<td><input type='text' name='kind' value={kind.str} row={index} onChange={e => onChange(e)}></td>
</tr>
))
Change value={kind.str} to value={rowData.kind.str}
Thanks everyone for answering my question. I was able to figure it out by splitting my components. So from my Items I render an Item component that renders the item and an Edit component that brings props in for that row only. By doing this I was able to eliminate the need for a row field which therefore gets rid of my error. React knows which row I am editing.