Change the file after uploading in React JS (Reupload files) - javascript

Here my code to upload multi files in React JS and show these files front of the user. .I have two buttons near of the name of the files : first button name:"Delete" when the user click it all the row disappear,and that works fine. second button name: "Change" when the user click it he can upload another file, and the new file must replace the old . How can I do that ?
import React from 'react';
import '../index.css';
import './dna.css';
export default class Browse extends React.Component {
state = {
files: []
};
fileUpload = (e) => {
console.log(e.target.files);
this.setState({ files: [...e.target.files] });
};
Change(id) {
console.log('Change Function');
}
Delete(name) {
this.setState((prevState) => ({
files: prevState.files.filter((file) => file.name !== name)
}));
console.log(this.state.files.name);
}
render() {
return (
<div className='Browse'>
<label>Insert DNA Files:</label>
<input
type='file'
multiple='multiple'
id='file'
onChange={this.fileUpload}
/>
<table className='filesName'>
{this.state.files.map((file, i) => (
<tr key={i}>
- <th style={{ textAlign: 'left' }}>{file.name} : </th>
<th>
<button type='file' onClick={() => this.Change(i)}>
Change
</button>
</th>
<th>
<button onClick={() => this.Delete(file.name)}>Delete</button>
</th>
</tr>
))}
</table>
</div>
);
}
}

Please check this example:
import React from "react";
import '../index.css';
// import './dna.css';
export default class Browse extends React.Component {
constructor(props) {
super(props);
this.state = {
files: [],
changedFileIndex: -1
};
this.fileUploaderRef = React.createRef();
}
fileUpload = (e) => {
let changedFile = e.target.files[0];
let uploadedFiles = e.target.files;
if (this.state.changedFileIndex >= 0) {
this.setState(prevState => {
const list = [];
prevState.files.map((file, i) => {
if (i === prevState.changedFileIndex)
list.push(changedFile);
else
list.push(file);
});
return {
files: list,
changedFileIndex: -1,
};
});
} else if (this.state.files.length > 0) {
this.setState(prevState => {
return {files: [...prevState.files, ...uploadedFiles]}
});
} else
this.setState({files: [...e.target.files]});
};
Change(index, file) {
console.log("Change Function");
this.setState({changedFileIndex: index});
this.fileUploaderRef.current.click();
}
Delete(name) {
this.setState(prevState => {
const list = [];
prevState.files.map((file, i) => {
if (file.name !== name) {
list.push(file);
}
});
return {
files: list,
changedFileIndex: -1,
};
});
}
render() {
return (
<div className="Browse">
<label>Insert DNA Files:</label>
<input type="file" multiple="multiple" id="file" ref={this.fileUploaderRef} onChange={this.fileUpload}/>
<table className="filesName">
<tbody>
{
this.state.files.map((file, i) =>
<tr key={i}>
<th style={{textAlign: "left"}}>{file.name} :</th>
<th>
<button type="file" onClick={() => this.Change(i)}>Change</button>
</th>
<th>
<button onClick={() => this.Delete(file.name)}>Delete</button>
</th>
</tr>
)
}
</tbody>
</table>
</div>
);
}
}

Related

Why does my delete button not remove an item from my repository?

I am trying to use this.remove() in order to remove an item from a JPA repository, but for some reason it does not work. Below is the code from the JavaScript page that contains the delete button, with the specific line in bold:
import React, { Component } from 'react';
import '../css/TaskList.css';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import Select from "react-select/creatable";
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
class TaskList extends Component {
constructor(props) {
super(props);
this.state = {tasks: [], isLoading: true};
this.remove = this.remove.bind(this);
}
componentDidMount() {
fetch('/tasks')
.then(response => response.json())
.then(data => this.setState({tasks: data, isLoading: false}));
}
async remove(id) {
await fetch(`/tasks/${id}`, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(() => {
let updatedTasks = [...this.state.tasks].filter(i => i.id !== id);
this.setState({tasks: updatedTasks});
});
}
render() {
const {tasks, isLoading} = this.state;
//if waiting for tasks to fetch
if (isLoading) {
return <p>Loading...</p>;
}
//options for the sort select menu
const options = [
{value: 'priority', label: 'Priority'},
{value: 'type', label: 'Type'},
{value: 'class', label: 'Class'},
{value: 'date', label: 'Due Date'}
]
//method to be called when a new sort selection is made
const handleSortSelectChange = (option) => {
let updatedTasks;
switch (option.value) {
case 'priority':
updatedTasks = tasks.sort((a, b) => b.priority - a.priority);
break;
case 'date':
updatedTasks = tasks.sort((a, b) => a.dueDate - b.dueDate);
break;
case 'class':
//TODO implement sort by class
console.log('Sort by class');
updatedTasks = tasks;
break;
case 'type':
updatedTasks = tasks.sort((a, b) => b.typeID - a.typeID);
break;
default:
updatedTasks = tasks.sort((a, b) => a.dueDate - b.dueDate);
}
this.setState({tasks: updatedTasks});
}
//component that maps all tasks to <tr> elements
const taskList = tasks.map(task => {
const date = task.dueDate;
return (
<tr key={task.id} onClick={"/tasks/" + task.id}>
<td style={{whiteSpace: 'nowrap'}}><a href={"/tasks/" + task.id}>{task.name}</a></td>
<td className='task-priority'>{getPriorityString(task.priority)}</td>
<td>{task.courseID}</td>
<td>{task.type}</td>
<td>{date}</td>
<td>
** <ButtonGroup>
<Button size="sm" color="danger" onClick={() => this.remove(task.id)}>Delete</Button>**
</ButtonGroup>
</td>
</tr>
);
});
return (
<div>
<AppNavbar/>
<Container fluid>
<h3 className='m-2'>Tasks</h3>
<div id="list-top" className="m-2">
<Button color="success" tag={Link} to="/tasks/new">Add Task</Button>
</div>
<div id="list-sort" className="m-2">
<Select className='m-2' options={options} onChange={handleSortSelectChange}/>
</div>
<div id="list-top" className="m-2">
<h5 className='m-2'>Sort By</h5>
</div>
<Table className="m-2">
<thead>
<tr>
<th width="30%">Name</th>
<th width="10%">Priority</th>
<th width="20%">Class</th>
<th width="10%">Type</th>
<th width="20%">Due Date</th>
<th width="10%"></th>
</tr>
</thead>
<tbody>
{taskList}
</tbody>
</Table>
</Container>
</div>
);
}
}
function getPriorityString(priority) {
let result = "";
let i;
for (i = 0; i < priority; i++) {
result += "!";
}
return result;
}
export default TaskList;
I've tried looking around online and at other forums to see if I am doing something wrong, but everything I have found says that this should work. Everything else works within the program, so I'm at a loss as to why this specific function does not.

pass id in react.js to make update view

I have listing view working correctly and I want to pass data to update view by Id - in URL Id is passed correctly, but without data saved to this Id. In console log id is tagged as undefined
UpdateCar.jsx
import React, { Component } from 'react';
import CarServices from '../../Services/CarServices';
class UpdateCar extends Component {
constructor(props) {
super(props)
this.state = {
carId: this.props.match.id,
brand: '',
color: ''
}
this.changeBrandHandler = this.changeBrandHandler.bind(this);
this.changeColorHandler = this.changeColorHandler.bind(this);
this.getCarId = this.getCarId.bind(this);
this.updateCar = this.updateCar.bind(this);
}
componentDidMount() {
CarServices.getCarById(this.state.carId).then((res) => {
let car = res.data;
this.setState({
brand: car.brand,
color: car.color
});
});
}
changeBrandHandler = (event) => {
this.setState({ brand: event.target.value });
}
changeColorHandler = (event) => {
this.setState({ color: event.target.value });
}
updateCar = (e) => {
e.preventDefault();
let car = { brand: this.state.brand, color: this.state.color };
console.log('test: ' + JSON.stringify(car));
console.log('id => ' + JSON.stringify(car.carId));
}
cancel() {
this.props.history.push('/showCars');
}
render() {
return (
<div>
<div className='container'>
<div className='row'>
<div className='card col-md-6 offset-md-3 offset-md-3'>
<h3 className='text-center'> Edit car </h3>
<div className='cardBody'>
<form>
<div className='form-group'>
<label> Brand: </label>
<input placeholder="brand" name="brand" className="form-control"
value={this.state.brand} onChange={this.changeBrandHandler} />
<label> Color: </label>
<input placeholder="color" name="color" className="form-control"
value={this.state.color} onChange={this.changeColorHandler} />
</div>
<button className="btn btn-success" onClick={this.updateCar}>Save</button>
<button className="btn btn-danger" onClick={this.cancel.bind(this)} style={{ marginLeft: "10px" }}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default UpdateCar;
CarServices.js
When I hardcode id in url it works correclty - I don't know why I don't have any data from database in placeholders
import axios from 'axios';
const CAR_API_BASE_URI = "http://localhost:8080/car"
class CarServices{
getCars(){
return axios.get(CAR_API_BASE_URI);
}
addCar(car){
return axios.post(CAR_API_BASE_URI, car);
}
getCarById(id){
return axios.get(CAR_API_BASE_URI + '/' + id);
//return axios.get("http://localhost:8080/car/2"); - correclty view data from database saved with id:2
}
}
export default new CarServices();
ListCar.jsx
import React, { Component } from 'react';
import CarServices from '../../Services/CarServices';
class ListCar extends Component {
constructor(props){
super(props)
this.state = {
cars: []
}
this.addCar = this.addCar.bind(this);
this.editCar = this.editCar.bind(this);
}
addCar(){
this.props.history.push('/addCar');
}
editCar(id){
this.props.history.push(`/editCar/${id}`);
}
componentDidMount(){
CarServices.getCars().then((res)=>{
this.setState({ cars: res.data})
})
}
render() {
return (
<div>
<h2 className='text-center'>Car list </h2>
<div className='row'>
<button className='btn btn-primary' onClick={this.addCar} style={{marginLeft: "15px"}} >Add car</button>
</div>
<div className='row'></div>
<table className='table table-striped table-bordered'>
<thead>
<tr>
<th className='text-center'>Id</th>
<th className='text-center'>brand</th>
<th className='text-center'>color</th>
<th className='text-center'>action</th>
</tr>
</thead>
<tbody>
{
this.state.cars.map(
car =>
<tr key = {car.carId}>
<td className='text-center'>{car.carId}</td>
<td className='text-center'>{car.brand}</td>
<td className='text-center'>{car.color}</td>
<td className='text-center'>
<button onClick ={ () => this.editCar(car.carId)} className="btn btn-info">Update </button>
<button style={{marginLeft: "10px"}} className="btn btn-danger">Delete </button>
<button style={{marginLeft: "10px"}} className="btn btn-info">View </button>
</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
}
export default ListCar;
Assuming the UpdateCar component is correctly rendered on a Route and receives the route props, the issue is likely that this.props.match.id won't necessarily be defined in the constructor when the component is mounting.
If you must reference props in the constructor then reference the passed props argument.
constructor(props) { // <-- reference this props
super(props)
this.state = {
carId: props.match.id, // reference props arg
brand: '',
color: ''
}
...
}
It's better to reference props directly though as it's considered anti-pattern in React to store props into local state.
componentDidMount() {
const { match } = this.props;
if (match.id) {
CarServices.getCarById(match.id)
.then((res) => {
const { brand, color } = res.data;
this.setState({ brand, color });
})
.catch(error => {
// catch and handle any Promise rejections or thrown errors
});
}
}
Don't forget to also handle the id route path parameter changing while the UpdateCar component is mounted.
componentDidUpdate(prevProps) {
const { match } = this.props;
if (prevProps.match.id !== match.id) {
CarServices.getCarById(match.id)
.then((res) => {
const { brand, color } = res.data;
this.setState({ brand, color });
})
.catch(error => {
// catch and handle any Promise rejections or thrown errors
});
}
}

How to group by two columns? ReactJS

The code that I posted below is the API request from which I make a table. This table has 4 columns: id, userid, title. I want to understand how I can sort by userid and title, as shown in the photo. It would be great if the steps were described in detail.
I'm trying to group the tab as shown in the photo, but I can't.
Can you suggest/show me how to do this?
Also wanted to know how to reset the group value of a column?
I will be grateful for any help.
My code:
import React from "react";
import "./GroupByUserID.css";
import { Link } from "react-router-dom";
export default class GroupByUserID extends React.Component {
// Constructor
constructor(props) {
super(props);
this.state = {
items: [],
};
}
componentDidMount = () => {
this.apiFetch();
};
//Fetch data from API
apiFetch = () => {
return fetch("https://jsonplaceholder.typicode.com/todos")
.then((res) => res.json())
.then((json) => {
this.setState((prevState) => {
return { ...prevState, items: json };
});
});
};
// Sort UserID
setSortedItemsUserID = () => {
const { items } = this.state;
const sortedUserID = items.sort((a, b) => {
if (a.userId < b.userId) {
return items.direction === "ascending" ? -1 : 1;
}
if (a.userId > b.userId) {
return items.direction === "ascending" ? 1 : -1;
}
return 0;
});
console.log(sortedUserID);
this.setState((prevState) => {
return { ...prevState, items: sortedUserID };
});
};
render() {
const { items } = this.state;
return (
<div>
<h1>Home Page</h1>
<table>
<thead>
<tr>
<th>
<Link target="self" to="/">
View Normal
</Link>
</th>
<th>Group By UserID</th>
</tr>
</thead>
<thead>
<tr>
<th>
User ID
<button
type="button"
onClick={() => this.setSortedItemsUserID()}
>
⬇️
</button>
</th>
<th>Title</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.userId + item.title}>
<td>{item.userId}</td>
<td>{item.title}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}

Delete Table Row not working properly with search bar

i'm new to reactjs and i'm trying to make a table that shows the information from a array of objects and have a button of delete and an input to search among the users. The delete button is working correctly when i'm not searching anything, but when i'm searching it doesn't delete the corretly row, and deletes only the first one. I see that it is because the arrays that show the table are different with and without the search being used but I don't know how to make it work.
this is the component of the table:
import { formatDate } from "../../utils/formatDate";
import "./table.css";
import { useState } from "react";
function Table(props) {
const { headerData, bodyData, type, removeItem} = props;
const isUser = type === "user";
const buildTableItems = () => {
return bodyData.map((item, index) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
<td>
<button className="delete-button" onClick={() => removeItem(index)}>
Delete
</button>
</td>
</tr>
));
};
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th >{headerTable}</th>
))}
</tr>
</thead>
<tbody>{buildTableItems()}</tbody>
</table>
</div>
);
}
export default Table;
Here the component of the search bar:
import "./searchBar.css"
function SearchBar({ searchedData, onSearch }) {
return (
<div className="search-bar">
<label>Search</label>
<input type="text" placeholder="Search User" value={searchedData} onChange={e => onSearch(e.target.value)} />
</div>
);
}
export default SearchBar;
and here is the home:
import "./Home.css";
import React, { useEffect, useState } from "react";
import Header from "../components/Header/Header";
import Table from "../components/Table/Table";
import AddData from "../components/AddData/AddData";
import SearchBar from "../components/SearchBar/SearchBar";
import { userArr } from "../mock/users";
const Home = () => {
const headerUser = ["Name", "Email", "Occupation", "Birthday"];
const [newUserArr, setNewUserArr] = useState(userArr);
const [searchedItem, setSearchedItem] = useState("");
const searchedArray = newUserArr.filter((item) => {
if (item.name.toLowerCase().includes(searchedItem.toLowerCase())) {
return true;
}
});
function onSearch(e) {
setSearchedItem(e);
}
const addDataToArr = (form) => {
setNewUserArr([...newUserArr, form]);
};
const deleteData = (indexUserArr) => {
let restOfDataArray = newUserArr.filter(
(element, ind) => ind !== indexUserArr
);
setNewUserArr(restOfDataArray);
};
return (
<>
<Header />
<SearchBar searchedData={searchedItem} onSearch={onSearch} />
<Table
type="user"
headerData={headerUser}
bodyData={newUserArr}
removeItem={(index) => deleteData(index)}
/>
<AddData saveData={(val) => addDataToArr(val)} />
</>
);
};
export default Home;
thank you
If you have ID in your user data then use that instead of index or create id keywords using concatenate with your values here is examples.
import { formatDate } from "../../utils/formatDate";
import "./table.css";
import { useState } from "react";
function Table(props) {
const { headerData, bodyData, type, removeItem} = props;
const isUser = type === "user";
const buildTableItems = () => {
return bodyData.map((item, index) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
<td>
<button className="delete-button" onClick={() => removeItem(`${item.name}${item.email}${item.occupation}`)}>
Delete
</button>
</td>
</tr>
));
};
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th >{headerTable}</th>
))}
</tr>
</thead>
<tbody>{buildTableItems()}</tbody>
</table>
</div>
);
}
export default Table;
And here is your delete method ${item.name}${item.email}${index}
const deleteData = (data) => {
let restOfDataArray = newUserArr.filter(
(element, ind) => `${element.name}${element.email}${element.occupation}` !== data
);
setNewUserArr(restOfDataArray);
};
This will fixed your problem. If this doesn't work then you need to use ID to resolve this problem. There is a possibility that ${item.name}${item.email}${item.occupation} can be duplicates.
Never use index ever for deleting or any other operations. Use always ID.

display button upon typing input react

I want to be able to type into my input fields, and then have a button show up beside it upon typing that says submit edit. right now, I have a button that always is there, but I want it to only show up upon typing. this is all in react btw. so far, I have tried jquery, but react doesn't like it.
here's the whole page, to avoid any confusion of what I am doing and where my stuff is located.
import React, { Component } from "react";
import axios from "axios";
import "../styles/TourPage.css";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true,
};
}
componentDidMount() {
axios
.get("/getResults")
.then((res) => {
this.setState({
myData: res.data
});
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
deleteById = (id) => {
console.log(id)
axios
.post(`/deleteDoc`, {id: id} )
.then(() => {
console.log(id, " worked")
window.location = "/tour"
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
}
editById = (id, siteLocation, Services, cnum) => {
console.log(id, siteLocation, Services, cnum)
axios
.post(`/editDoc`, JSON.stringify({id: id, location: siteLocation, Services: Services, cnum: cnum}),{
headers: {
"Content-Type": "Application/json"
}
} )
.then(() => {
console.log(id, " worked")
window.location = "/tour"
})
.catch((error) => {
// Handle the errors here
console.log(error);
})
}
render() {
// You can handle the loader part here with isLoading flag. In this case No data found will be shown initially and then the actual data
let { myData, isLoading } = this.state;
return (
<table id="customers">
<tr>
<th>siteLocation</th>
<th>Services</th>
<th>cnum</th>
</tr>
{myData.length > 0
? myData.map(({ location, Services, cnum, _id }, index) => (
<tr key={index}>
<td><input type="text" placeholder={location} name="location" id="location" /> </td>
<td><input type="text" placeholder={Services} name="Services" id="Services" /> </td>
<td><input type="text" placeholder={cnum} name="cnumhide" id="cnumhide" /> </td>
<td><input type="hidden" placeholder={cnum} name="cnum" id="cnum" /> </td>
<button
onClick={(e) => {
e.preventDefault();
this.deleteById(_id);
}}
disabled={isLoading}
>
Delete
</button>
<button
onClick={(e) => {
e.preventDefault();
var siteLocation = document.getElementById('location').value
var Services = document.getElementById('Services').value
var cnum = document.getElementById('cnum').value
this.editById(_id, siteLocation, Services, cnum)
}}
>
Submit Edit
</button>
</tr>
))
: "No Data Found"}
</table>
);
}
}
const script = document. createElement("script"); $('input').keyup(function(){
if($.trim(this.value).length > 0)
$('#location').show()
else
$('#location').hide()
});
export default TourPage;
thanks 4 the help in advance.
You can use onfocus() in the text element. If you want to hide the button, use onfocusout() or in case if you want to track only after input has changed, use onchange() event
...
//class function
onTyping =()=>{
this.setState({
showSubmit:true
})
}
...
//render part
render(){
...
//input which you want to track typing
<input type="text" onfocus={()=>this.onTyping()} placeholder={location} name="location" id="location" />
...
//element submit button
{this.state.showSubmit && <button
onClick={(e) => {
e.preventDefault();
var siteLocation = document.getElementById('location').value
var Services = document.getElementById('Services').value
var cnum = document.getElementById('cnum').value
this.editById(_id, siteLocation, Services, cnum)
}}
>
Submit Edit
</button>}
...
Here is an example that helps you,
const {
useState
} = React;
const Test = () => {
const [show, setShow] = useState(false);
const handleChange = (event) => {
if (event.target.value.length > 0)
setShow(true);
else
setShow(false)
}
return ( <div>
<input type = "text"
onChange = {
(event) => handleChange(event)
}/>
{show && < button > Submit changes now! </button>}
</div>
)
}
ReactDOM.render( < Test / > ,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
There is a way to avoid jquery and continue using your react class component to achieve this.
Map over state.myData to render each item with an input and a button.
Use the array index with the input's onChange event callback to add the inputValue into the correct array item's object within state.
Use the array index with the button's onClick event callback to get the item from state.myData before sending it to the server.
If there is an inputValue for the item, you can conditionally render the button.
import React, { Component } from "react";
import axios from "axios";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => {
this.setState({
myData: res.data.results
});
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
handleChangeInput = ({ target }, index) => {
const newData = [...this.state.myData];
newData[index].inputValue = target.value;
this.setState({
myData: newData
});
};
handleSubmitEdit = (index) => {
const item = this.state.myData[index];
// submit the edit to the api
console.log(
`Clicked on 'submit edit' for ${item.name} with value ${item.inputValue}`
);
};
render() {
let { myData, isLoading } = this.state;
if (isLoading) {
return "loading...";
}
return (
<div>
{myData.map(({ name, status, species, inputValue }, index) => {
return (
<div key={index}>
<p>{`${name} - ${species} - ${status}`}</p>
<input
type="text"
onChange={(e) => this.handleChangeInput(e, index)}
value={inputValue || ""}
/>
{inputValue && (
<button onClick={() => this.handleSubmitEdit(index)}>
Submit Edit
</button>
)}
</div>
);
})}
</div>
);
}
}
export default TourPage;
If you wanted to have an input per field within each row, you could make some small changes and save your edits to the item's state within a nested object.
Then you could check if there was anything inside that row's edits object to conditionally show the submit button per row.
import React, { Component } from "react";
import axios from "axios";
import isEmpty from "lodash.isempty";
import pick from "lodash.pick";
class TourPage extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
isLoading: true
};
}
componentDidMount() {
axios
.get("https://rickandmortyapi.com/api/character")
.then((res) => {
this.setState({
// here we create an empty 'edits' object for each row
myData: res.data.results.map((d) => ({
...pick(d, ["name", "status", "species"]),
edits: {}
}))
});
})
.finally(() => {
this.setState({
isLoading: false
});
});
}
handleChangeInput = ({ target }, index) => {
const newData = [...this.state.myData];
const { value, name } = target;
newData[index].edits[name] = value;
this.setState({
myData: newData
});
};
handleSubmitEdit = (index) => {
const item = this.state.myData[index];
// submit the edit to the api
console.log(`Clicked on 'submit edit' for ${item.name} with edits:`);
console.log(item.edits);
console.log("Updated item: ");
const { edits, ...orig } = item;
const newItem = { ...orig, ...edits };
console.log(newItem);
// Once saved to api, we can update myData with newItem
// and reset edits
const newData = [...this.state.myData];
newData[index] = { ...newItem, edits: {} };
this.setState({
myData: newData
});
};
showButton = (index) => {
const { edits } = this.state.myData[index];
return !isEmpty(edits);
};
render() {
let { myData, isLoading } = this.state;
if (isLoading) {
return "loading...";
}
return (
<table>
<tbody>
{myData.map((row, index) => {
const { edits, ...restRow } = row;
return (
<tr key={index}>
{Object.keys(restRow).map((col) => {
return (
<td>
<label>
{col}:
<input
name={col}
value={edits[col] || restRow[col]}
onChange={(e) => this.handleChangeInput(e, index)}
/>
</label>
</td>
);
})}
<td>
{this.showButton(index) && (
<button onClick={() => this.handleSubmitEdit(index)}>
Submit Edit
</button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
);
}
}
export default TourPage;

Categories

Resources