I have a table view in which I am fetching data from an API and then populating that data inside a table.
For some reason, the data inside tbody is not rendering and there are no errors either. I think it could be because I am not destructuring it correctly. But, unable to rectify it.
PS: the console logs inside tbody does work and they do display the information in the console.
UPDATE: doing the same thing using a simple table from reactstrap and that one works. But the first one with the sorting table is not working. Not sure where I am going wrong. I WOULD LIKE TO GO WITH THE SORTING TABLE PREFERABLY.
Code for Table View
import React from "react";
import axios from "axios";
// reactstrap components
import {
Card,
CardHeader,
CardBody,
CardTitle,
Row,
Col,
Table,
} from "reactstrap";
// core components
import SortingTable from "components/SortingTable/SortingTable.js";
class RegularTables extends React.Component {
constructor(props) {
super(props);
this.state = {
siteData: [],
isLoading: false,
};
}
signal = axios.CancelToken.source();
componentDidMount() {
this.handleGetEdgeSeverInfo();
}
componentUnMount() {
this.signal.cancel("Api is being canceled");
}
handleGetEdgeSeverInfo = async () => {
this.setState({ isLoading: true });
await axios
.get("http://www.mocky.io/v2/5ec3786f300000800039c0a5")
.then((response) => {
// handle success
this.setState({ siteData: response.data });
})
.catch((error) => {
// handle error
if (axios.isCancel(error)) {
console.log("Unable to fetch", error.message);
} else {
this.setState({ isLoading: false });
}
});
};
render() {
const { siteData } = this.state;
return (
<>
<div className="content">
<Row>
<Col className="mb-5" md="12">
<Card>
<CardHeader>
<CardTitle tag="h4">
table (sorting table)
</CardTitle>
<hr />
</CardHeader>
<CardBody>
<SortingTable
thead={[
{ text: "Ship" },
{ text: "technology" },
{ text: "https" },
{ text: "type" },
{ text: "Status" },
]}
tbody={siteData.map((data) => {
console.log("name:", data.site.name);
console.log("type:", data.https);
console.log("IMO:", data.site.attributes.type);
console.log("model:", data.technology);
console.log("status:", data.status);
return (
<div>
<tr key={data.site}>
<td>{data.site.name}</td>
<td>{data.kind}</td>
<td>{data.site.attributes.IMO}</td>
<td>{data.model}</td>
<td>{data.status}</td>
</tr>
</div>
);
})}
/>
</CardBody>
</Card>
</Col>
<Col className="mb-5" md="12">
<Card>
<CardHeader>
<CardTitle tag="h4">
table (simple table from reactstrap)
</CardTitle>
<hr />
</CardHeader>
<CardBody>
<Table>
<thead>
<tr>
<th>Ship</th>
<th>Type</th>
<th>IMO</th>
<th>Model</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{siteData.map((site) => {
console.log("name:", data.site.name);
console.log("type:", data.https);
console.log("IMO:", data.site.attributes.type);
console.log("model:", data.technology);
console.log("status:", data.status);
return (
<div>
<tr key={data.site}>
<td>{data.site.name}</td>
<td>{data.kind}</td>
<td>{data.site.attributes.IMO}</td>
<td>{data.model}</td>
<td>{data.status}</td>
</tr>
</div>
);
})}
</tbody>
</Table>
</CardBody>
</Card>
</Col>
</Row>
</div>
</>
);
}
}
export default RegularTables;
Code for sorting table component
class SortingTable extends React.Component {
constructor(props) {
super(props);
this.state = {
bodyData: props.tbody,
column: {
name: -1,
order: "",
},
};
}
sortTable = (key) => {
let { bodyData, column } = this.state;
let order = "";
if (
(column.name === key && column.order === "desc") ||
column.name !== key
) {
order = "asc";
bodyData.sort((a, b) =>
a.data[key].text > b.data[key].text
? 1
: a.data[key].text < b.data[key].text
? -1
: 0
);
} else if (column.name === key && column.order === "asc") {
order = "desc";
bodyData.sort((a, b) =>
a.data[key].text > b.data[key].text
? -1
: a.data[key].text < b.data[key].text
? 1
: 0
);
}
this.setState({
bodyData: bodyData,
column: {
name: key,
order: order,
},
});
};
render() {
const { bodyData, column } = this.state;
return (
<Table className="tablesorter" responsive>
<thead className="text-primary">
<tr>
{this.props.thead.map((prop, key) => {
return (
<th
className={classnames(
"header",
{
headerSortDown:
key === column.name && column.order === "asc",
},
{
headerSortUp:
key === column.name && column.order === "desc",
},
{
[prop.className]: prop.className !== undefined,
}
)}
key={key}
onClick={() => this.sortTable(key)}
>
{prop.text}
</th>
);
})}
</tr>
</thead>
<tbody>
{bodyData.map((prop, key) => {
return (
<tr
className={classnames({
[prop.className]: prop.className !== undefined,
})}
key={key}
>
{prop.data.map((data, k) => {
return (
<td
className={classnames({
[data.className]: data.className !== undefined,
})}
key={k}
>
{data.text}
</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
);
}
}
SortingTable.propTypes = {
thead: PropTypes.arrayOf(
PropTypes.shape({
className: PropTypes.string,
text: PropTypes.string.isRequired,
})
).isRequired,
tbody: PropTypes.arrayOf(
PropTypes.shape({
className: PropTypes.string,
data: PropTypes.arrayOf(
PropTypes.shape({
className: PropTypes.string,
text: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
})
),
})
).isRequired,
};
I think you need to return a single <tr> in siteData.map. Try this in CardBody
<CardBody>
<SortingTable
thead={[
{ text: "site" },
{ text: "https" },
{ text: "technology" },
{ text: "type" }
]}
tbody={siteData.map(data => {
console.log("name:", data.site.name);
console.log("type:", data.https);
console.log("IMO:", data.site.attributes.type);
console.log("model:", data.technology);
console.log("status:", data.status);
return (
<tr key={data.site}>
<td>{data.site.name}</td>
<td>{data.https}</td>
<td>{data.site.attributes.type}</td>
<td>{data.technology}</td>
<td>{data.status}</td>
</tr>
);
})}
/>
</CardBody>;
In SortingTable.render()
render() {
const { bodyData, column } = this.state;
return (
<Table className="tablesorter" responsive>
<thead className="text-primary">
<tr>
{this.props.thead.map((prop, key) => {
return (
<th
// className={classnames(
// "header",
// {
// headerSortDown:
// key === column.name && column.order === "asc"
// },
// {
// headerSortUp:
// key === column.name && column.order === "desc"
// },
// {
// [prop.className]: prop.className !== undefined
// }
// )}
key={key}
onClick={() => this.sortTable(key)}
>
{prop.text}
</th>
);
})}
</tr>
</thead>
<tbody>
{bodyData.map((prop, key) => {
return (
<tr
// className={classnames({
// [prop.className]: prop.className !== undefined
// })}
key={key}
>
{prop.props.children.map((data, k) => {
return (
<td
// className={classnames({
// [data.className]: data.className !== undefined
// })}
// key={k}
>
{data.props.children}
</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
);
}
I Commented out some code to easily reproduce the issue
Related
What I'm trying to pass true/false in another component. The user has the possibility to add languages(max2). When the user adds 2 same languages, the validation error will be shown, which to do that I have used some() method which returns true or false.
I tried it with ```localStorge``, but it didn't work.
import React from "react";
export default class AddLanguage extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "",
items: [],
hasError: ""
};
}
updateMessage(event) {
this.setState({
message: event.target.value
});
}
handleClick() {
var items = this.state.items;
items.push(this.state.message);
this.setState({
items: items,
message: ""
});
}
handleItemChanged(i, event) {
const items = event.target.value;
console.log("items", items);
this.setState((prev) => {
const currList = prev.items;
console.log("currList", currList);
const isDuplicate = currList.some(
(lang, idx) => idx !== i && lang) === items
);
if (isDupliacte) {
return { ...prev, hasError: "A BIG ERROR" };
} else {
currList[i] = items;
return { ...prev, items: currList };
}
});
}
handleItemDeleted(i) {
var items = this.state.items;
items.splice(i, 1);
this.setState({
items: items
});
}
renderRows() {
var context = this;
return this.state.items.map(function (o, i) {
return (
<tr key={"item-" + i}>
<td>
<div>
<input
type="text"
value={o}
autoComplete="off"
onChange={context.handleItemChanged.bind(context, i)}
/>
</div>
{this.state.hasError && (
<div>
<label></label>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
className="inline-block mr-2 h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
</svg>{" "}
{this.state.hasError}
{""}
</div>
</div>
)}
</td>
<td >
<button
type="button"
onClick={context.handleItemDeleted.bind(context, i)}
>
Delete
</button>
</td>
</tr>
);
}, this);
}
render() {
return (
<div>
<div>
<table>
<thead">
<tr>
<th
>
Button
</th>
<th
>
Info
</th>
<th
>
ACTION
</th>
</tr>
</thead>
<tbody>
{this.renderRows()}
</tbody>
</table>
</div>
<div>
<button
className="btn-main"
disabled={this.state.items.length >= 2}
onClick={this.handleClick.bind(this)}
>
<PlusSmIcon />
Add
</button>
</div>
</div>
);
}
}
What I want to pass to the other component is isDuplicate (which might be true/false to another component.
hello iam following mosh hamedani course at some point i got stuck in uploading data in table
this is my table where title and genre is uploading where in stock and rate these are number not string are not uploading here is my table body
class TableBody extends Component {
render() {
const {data,columns} = this.props
console.log({data,columns})
return ( <tbody>
{data.map(item => <tr key={item._id}>
{columns.map(column => <td key={item._id + (column.path || column.key)}>{_.get(item,column.path)}</td>)}
</tr>
)}
</tbody>
);
}
}
data and columns are coming from movietable component here is the code
class MovieTable extends Component {
columns =[
{ path:'title',label:'Title'},
{ path:'genre.name',label:'Genre'},
{ path:'numberInstock',label:'stock'},
{ path:'dailyReantalRate',label:'Rate'},
{ key: 'like' },
{key: 'delete' }
];
render() {
const {movies, onDelete,onSort ,onLike,sortColumn,onAdd,deleted} = this.props;
return (
<table className="table">
<TableHeader columns={this.columns} sortColumn={sortColumn} onSort={onSort}/>
<TableBody data={movies} columns={this.columns}/>
<tbody>
{movies.map((movie) => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
{" "}
<Like
liked={movie.liked}
onClick={() => onLike(movie)}
/>{" "}
</td>
<td
onClick={() => onDelete(movie._id)}
className="btn btn-danger btn-outline-warning btn-sm active "
>
Remove
</td>
</tr>
))}
</tbody>
<tbody>
{deleted.map((movie) => (
<tr key={movie._id}>
<td>{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
{" "}
<Like />{" "}
</td>
<td
onClick={() => onAdd (movie._id)}
className="btn btn-danger btn-outline-primary btn-sm active "
>
ADD
</td>
</tr>
))}
</tbody>
</table>
);
}
}
movies from props coming from its parent movies component here is movies component code
class Movies extends Component {
state = {
movies:[],
deleted: [],
genres:[],
pageSize: 9,
currentPage:1,
sortColumn:{
path:'title',
order:'asc'
}
};
componentDidMount(){
const genres =[{ _id:"",name:'All Genres'},...getGenres()]
this.setState({
movies:getMovies(),
genres
})
}
handleDelete = (_id) => {
const movie = this.state.movies.find((x) => x._id === _id);
this.setState({ deleted: [...this.state.deleted, movie] });
this.setState({ movies: this.state.movies.filter((x) => x._id !== _id) });
};
handleLike = (m) => {
const movies = [...this.state.movies];
const index = movies.indexOf(m);
movies[index] = { ...movies[index] };
movies[index].liked = !movies[index].liked;
this.setState({ movies });
};
handleReinstate = (_id) => {
const movie = this.state.deleted.find((movie) => movie._id === _id);
this.setState({ movies: [...this.state.movies, movie] });
this.setState({
deleted: this.state.deleted.filter((movie) => movie._id !== _id),
});
};
handleGenreSelect = genre => {
this.setState({selectedGenre:genre, currentPage:1})
}
handleSort= sortColumn =>{
this.setState({sortColumn});
}
render() {
const { pageSize,currentPage,sortColumn,selectedGenre,movies:allMovies,deleted} = this.state;
const filtered = selectedGenre && selectedGenre._id ? allMovies.filter(m=>m.genre._id === selectedGenre._id ): allMovies;
const sorted = _.orderBy(filtered, [sortColumn.path],[sortColumn.order]);
const movies = paginate(sorted,currentPage,pageSize)
return (
<div className="row">
<div className="col-2">
<ListGroup items={this.state.genres} selectedItem={this.state.selectedGenre} onItemSelect={this.handleGenreSelect}/>
</div>
<div className="col">
<div className={this.getbadgesClasses()}> <p>there are {filtered.length} movies in our data base</p> </div>
<MovieTable
movies={movies}
onSort={this.handleSort}
onDelete={this.handleDelete}
onLike={this.handleLike}
deleted={deleted}
onAdd={this.handleReinstate}/>
<Pagination
itemCount={filtered.length}
pageSize={pageSize}
sortColumn={sortColumn}
onPageChange={this.handlePageChange}
currentPage={currentPage}
/>
</div>
</div>
);
}
getbadgesClasses() {
let classes = " badge m-2 badge-";
classes += this.state.movies.length === 0 ? "warning" : "primary";
return classes;
}
handlePageChange = (page) => {
this.setState({currentPage: page})
};
}
this is my console.log
i have give aerong path to Columns array its like spelling mistake in path
The grid before sorting is as follows:
Now I am going to sort it with Patient ID
Now when I check the Patient ID: 936447 which is in last position after sorting the grid returns to it's original ordering.
But I want the sorted grid even after checking the checkbox/ selecting a row.
This the code for displaying the grid and sorting the grid.
class App extends Component {
constructor(props) {
super(props);
this.state = {
order: {},
orderby: '',
printQueueList: props.printQueueList && props.printQueueList,
};
this.sort = this.sort.bind(this);
}
componentWillReceiveProps(nextProps) {
if (this.state.printQueueList != nextProps.printQueueList)
this.setState({ printQueueList: nextProps.printQueueList && nextProps.printQueueList });
}
sort(event) {
const { order } = this.state;
let { printQueueList } = this.props;
var gridData = printQueueList;
order[event.target.id] = !order[event.target.id];
gridData = _.orderBy(gridData, (o) => typeof o[event.target.id] === 'string' ? o[event.target.id].trim().toLowerCase() : o[event.target.id], order[event.target.id] ? 'asc' : 'desc');
this.setState({
orderby: event.target.id,
printQueueList: gridData,
order
});
}
render() {
return (
<div className="App">
<table >
<thead >
<tr >
<th id="select" >
<Checkbox
input={{
name: 'selectAll',
onChange: //function
value: allSelected
}}
/>
<label htmlFor="SelectAll" >Select All</label>
</th>
<th id="PatientID" onClick={this.sort}>Patient ID {order.PatientID ? <i id="PatientID" className="fa fa-sort-asc" /> : <i id="PatientID" className="fa fa-sort-desc" />}</th>
<th id="DocType" onClick={this.sort}>Type {order.DocType ? <i id="DocType" className="fa fa-sort-asc" /> : <i id="DocType" className="fa fa-sort-desc" />}</th>
</tr>
</thead>
<tbody >
printQueueList && printQueueList.map((Queue, i) => {
return (
<tr key={Queue.PrintQueueID}
onClick={() => onSelectPrintQueueGrid(Queue.PrintQueueID)}>
<td >
<Checkbox
input={{
name: Queue.PrintQueueID,
onChange: //function
value: selectedPrintQueueList.indexOf(Queue.PrintQueueID) !== -1,
}}
/>
</td>
<td className="dashboard_table-cell" title={'Patient ID:' + Queue.PatientID}>{Queue.PatientID}</td>
<td className="dashboard_table-cell" title={'Type:' + Queue.DocType}>{Queue.DocType}></td>
</tr>)
}
)
</tbody>
</table>
</div>
)
}
}
const mapStateToProps = state => {
return {
printQueueList: state.myDashboardReducer.printQueueList,
};
};
const mapDispatchToProps = dispatch => {
return {
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Since componentWillReceiveProps(nextProps) updates the state with the newProps, the array printQueueList gets updated. For that reason before updating with newProps, I checked whether the array is sorted or not.
If it isn't sorted, update printQueueList with newProps.
componentWillReceiveProps(nextProps) {
if(_.isEmpty(this.state.order)){
if (this.state.printQueueList != nextProps.printQueueList)
this.setState({ printQueueList: nextProps.printQueueList && nextProps.printQueueList });
}
}
I am having a List of Jobs with Approve button.
Expected Result
Whenever the Approve button next to a Job is clicked, the Job should be approved and the button text should say Approved.
Original Outcome
The text of all the buttons gets changed to Approved, but only a single Job gets approved, i.e. the job the was actually approved. Also the text stays approved for all the jobs until the page is refreshed.
export default class ProfilePage extends Component {
constructor() {
super();
this.state = {
allJobs: [],
loading: true,
newLoading: false,
valueApprove: "Approve",
_id: ""
};
this.handleApproveJob = this.handleApproveJob.bind(this);
}
componentDidMount() {
var apiUrl = `http://localhost:5000/admin/pendingJobs`;
fetch(apiUrl)
.then(response => {
return response.json();
})
.then(data => {
let allJobsFromApi = data.PendingJobs.map(job => {
return {
jobmainid: job._id,
companyName: job.companyNane,
contactPerson: job.contactPerson,
jobTitle: job.jobTitle,
jobDescription: job.description,
jobCategory: job.category.name,
jobDuration: job.duration,
descriptionLink: job.descriptionLink,
status: job.status
}
})
this.setState({ allJobs: allJobsFromApi, loading: false });
})
.catch(error => {
console.log(error);
})
}
handleApproveJob(e) {
this.setState({
newLoading: true
})
e.preventDefault();
var jobId = e.target.getAttribute('jobId');
console.log(jobId);
const job = {
approvedJobId: jobId
};
approveJob(job).then((res, err) => {
if (res) {
this.setState({
newLoading: false,
valueApprove: "Approved"
})
} else {
this.setState({
loading: false,
message: "some error occured"
})
}
})
}
render() {
const { newLoading, valueApprove } = this.state;
return (
<div className="tile-body">
{loading ? <div className="col-md-4 offset-md-4"><img src=
{LoadingSpinner} /></div> : <table
className="table table-hover table-bordered table-responsive"
id="sampleTable"
style={{ border: "none" }}
>
<thead>
<tr>
<th>Company Name</th>
<th>Contact Person At Company</th>
<th>Job Title</th>
<th>Job Description</th>
<th>Job Description Link</th>
<th>Category</th>
<th>Duration</th>
<th>Edit</th>
<th>Approve</th>
</tr>
</thead>
<tbody>
{this.state.allJobs.map((job) =>
<tr key={job.jobmainid}>
<td>{job.companyName}</td>
<td>{job.contactPerson}</td>
<td>{job.jobTitle}</td>
<td>{job.jobDescription}</td>
<td>
{job.descriptionLink}
</td>
<td>{job.jobCategory}</td>
<td>{job.jobDuration}</td>
<td>
<Link to={"/admin/editJob/" + job.jobmainid}>
<button className="btn btn-warning">Edit</button>
</Link>
</td>
<td>
<button
type="submit"
className="btn btn-success"
jobId={job.jobmainid}
onClick={this.handleApproveJob}
>
{ newLoading ? <ButtonLoading /> : valueApprove }
</button>
</td>
</tr>
)}
</tbody>
</table>}
</div>
The code for aproveJob() function
//Approve Job
export const approveJob = job => {
return axios.post("http://localhost:5000/admin/approveJob", {
_id: job.approvedJobId
})
.then(response => {
return response.data;
})
.catch(err => {
console.log(err);
});
}
Right now you are only storing a single variable with the approved state. Instead we need one per button.
You could do something like the below code.
After getting the array jobs from the server inject a value approved: false
On button click find and update that item in your state with approved: true
I'm not certain the code below runs but it will point you in the right direction.
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
export const approveJob = (job) => {
return fetch('http://localhost:5000/admin/approveJob', {
_id: job.approvedJobId
})
.then((response) => {
return response.data
})
.catch((err) => {
console.log(err)
})
}
export default class ProfilePage extends Component {
constructor(props) {
super(props)
this.state = {
allJobs: [],
loading: true,
newLoading: false
}
this.handleApproveJob = this.handleApproveJob.bind(this)
}
componentDidMount() {
const apiUrl = `http://localhost:5000/admin/pendingJobs`
fetch(apiUrl)
.then((response) => {
return response.json()
})
.then((data) => {
const allJobsFromApi = data.PendingJobs.map((job) => {
return {
jobmainid: job._id,
companyName: job.companyNane,
contactPerson: job.contactPerson,
jobTitle: job.jobTitle,
jobDescription: job.description,
jobCategory: job.category.name,
jobDuration: job.duration,
descriptionLink: job.descriptionLink,
status: job.status,
approved: false
}
})
this.setState({ allJobs: allJobsFromApi, loading: false })
})
.catch((error) => {
console.log(error)
})
}
handleApproveJob(e, index) {
e.preventDefault()
this.setState({
newLoading: true
})
const jobId = e.target.getAttribute('jobId')
console.log(jobId)
const job = {
approvedJobId: jobId
}
approveJob(job).then((res) => {
if (res) {
const newAllJobs = this.state.allJobs
newAllJobs[index].approved = true
this.setState({
newLoading: false,
valueApprove: 'Approved',
allJobs: newAllJobs
})
} else {
this.setState({
loading: false,
message: 'some error occured'
})
}
})
}
render() {
const { loading, newLoading, valueApprove, allJobs } = this.state
return (
<div className="tile-body">
{loading ? (
<div className="col-md-4 offset-md-4">
<img src={LoadingSpinner} />
</div>
) : (
<table
className="table table-hover table-bordered table-responsive"
id="sampleTable"
style={{ border: 'none' }}
>
<thead>
<tr>
<th>Company Name</th>
<th>Contact Person At Company</th>
<th>Job Title</th>
<th>Job Description</th>
<th>Job Description Link</th>
<th>Category</th>
<th>Duration</th>
<th>Edit</th>
<th>Approve</th>
</tr>
</thead>
<tbody>
{allJobs.map((job, index) => (
<tr key={job.jobmainid}>
<td>{job.companyName}</td>
<td>{job.contactPerson}</td>
<td>{job.jobTitle}</td>
<td>{job.jobDescription}</td>
<td>
<a href={job.descriptionLink} target="_blank">
{job.descriptionLink}
</a>
</td>
<td>{job.jobCategory}</td>
<td>{job.jobDuration}</td>
<td>
<Link to={'/admin/editJob/' + job.jobmainid}>
<button className="btn btn-warning">Edit</button>
</Link>
</td>
<td>
<button
type="button"
className="btn btn-success"
jobId={job.jobmainid}
onClick={(e) => this.handleApproveJob(e, index)}
>
{newLoading ? <ButtonLoading /> : valueApprove}
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
)
}
}
try this.
handleApproveJob(e) {
this.setState({
newLoading: true
});
e.preventDefault();
var jobId = e.target.getAttribute('jobId');
console.log(jobId);
const job = {
approvedJobId: jobId
};
let self = this;
approveJob(job).then((res, err) => {
if (res) {
self.setState({
newLoading: false,
valueApprove: "Approved"
})
} else {
self.setState({
loading: false,
message: "some error occured"
})
}
})
}
<button type="submit" className="btn btn-success" jobId={job.jobmainid} onClick={this.handleApproveJob}>
{ this.state.newLoading ? <ButtonLoading /> : this.state.valueApprove }
</button>
I hope I've been there for you. Let me know.
Three things are there.
you need to add approve: false to every item in the jobs
On clicking approve button of any job, you need to pass index of that job in the approveJob(index) function.
Depending on the index value (which was passed in approveJob(index)) you need to updated the particular job in the job array in the state.
Just update the render function code for approve button, as you need change text on clicking button
render() {
const { loading, newLoading, valueApprove, allJobs } = this.state
return (
<div className="tile-body">
{loading ? (
<div className="col-md-4 offset-md-4">
<img src={LoadingSpinner} />
</div>
) : (
<table
className="table table-hover table-bordered table-responsive"
id="sampleTable"
style={{ border: 'none' }}
>
<tbody>
{allJobs.map((job, index) => (
<tr key={job.jobmainid}>
<td>
<button
type="button"
className="btn btn-success"
jobId={job.jobmainid}
onClick={(e) => this.handleApproveJob(e, index)}
>
{job.approved ? "approved" : "approve"}
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
)
}
}
Cannot execute function declared in the Class component which has been passed to a function component.
There is a openBookDetails declared in the Class and that executes without a problem. I used the same logic to implement the deleteQuote function but I get a TypeError: _deleteQuote is not a function
I am trying to execute a function within a TableRow when a button is clicked however I get a typeerror.
import Modal from '../Modal/Modal'
import add from '../../images/add.png'
import addSelected from '../../images/addSelected.png'
import './Dashboard.css'
const TableRow = ({ row, openBookDetails, deleteQuote }) => (
<tr >
<th scope="row" onClick={openBookDetails}>{row.author}</th>
<td onClick={openBookDetails}>{row.quote}</td>
<td><i className="fa fa-close" onClick={deleteQuote}></i></td>
</tr>
)
const Table = ({ data, openBookDetails, deleteQuote }) => (
<table className="table table-hover">
<thead>
<tr className="table-active">
<th scope="col">Author</th>
<th scope="col">Quote</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{data.map((row, index) =>
<TableRow key={index} row={row} openBookDetails={() => openBookDetails(row, index)} deleteQuote={() => deleteQuote()}/>
)}
</tbody>
</table>
)
class Dashboard extends Component {
constructor() {
super()
this.state = {
quotes: [
{
"quote": "Our industry does not respect tradition - it only respects innovation.",
"author": "Satya Nadella",
"position": "CEO of Microsoft"
},
{
"quote": "Engineering is the closest thing to magic that exists in the world.",
"author": "Elon Musk",
"position": "Tesla and SpaceX CEO"
},
{
"quote": "For me, it matters that we drive technology as an equalizing force, as an enabler for everyone around the world.",
"author": "Sundar Pichai",
"position": "CEO of Google"
}
],
addSource: add,
isModalOpen: false,
index: '',
author: '',
quote: ''
}
}
onAddMouseOver = () => {
this.setState({ addSource: addSelected })
}
onAddMouseOut = () => {
this.setState({ addSource: add })
}
toggleModalOpenOrClose = () => {
this.setState({ isModalOpen: !this.state.isModalOpen })
this.setState({index: ''})
this.setState({author: ''})
this.setState({quote: ''})
}
openBookDetails = (row, index) => {
console.log('Row Clicked');
// console.log(index);
// console.log(row)
// console.log(row.author)
// console.log(row.quote)
// console.log(this.state.quotes[row])
// console.log(index)
this.setState({ isModalOpen: true});
this.setState({ index: index });
this.setState({ author: row.author });
this.setState({ quote: row.quote });
}
deleteQuote = () => {
this.setState({isModalOpen: false})
console.log('Row deleted')
}
addNewQuote = () => {
var quote = {
"quote": "There is no corruption in the system. The system is correuption",
"author": "Unknown",
"position": "Unknown"
}
console.log(this.state)
var quotes = this.state.quotes.concat(quote);
this.setState({ quotes: quotes });
}
render() {
return (
<div class='pt-3'>
<Table
data={this.state.quotes}
openBookDetails={this.openBookDetails} />
<div className='text-center align-items-center justify-content-centerpt-5'>
<a href='#add' onClick={this.toggleModalOpenOrClose}>
<img src={this.state.addSource} className='addButton mx-1' alt="add" onMouseOver={this.onAddMouseOver} onMouseOut={this.onAddMouseOut} />
</a>
</div>
<Modal
isModalOpen={this.state.isModalOpen}
toggleModalOpenOrClose={this.toggleModalOpenOrClose}
state={this.state}
addNewQuote={this.addNewQuote} />
</div>
)
}
}
export default Dashboard
When I click the ```<i className="fa fa-close" onClick={deleteQuote}></i>``` inside of the ```TableRow``` component I expect it to execute the ```deleteQuote``` function
You need to pass deleteQuote function as a prop to Table component when you calling it in Dashboard component in order to access it in Table component
So you need to change below line in Dashboard component
Change
<Table
data={this.state.quotes}
openBookDetails={this.openBookDetails} />
To
<Table
data={this.state.quotes}
openBookDetails={this.openBookDetails}
deleteQuote={this.deleteQuote} />