How to change the text of a single button in Reactjs - reactjs

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>
)
}
}

Related

uploading data to table getting no result

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

No class .db.model.FileDB entity with id 0 exists?

I have a REST API in Spring Boot with JPA and Spring Security ,I manage to upload a file and download it successfully but when I try to delete an existing user object in my database by ID, I get the following error message:
org.springframework.dao.EmptyResultDataAccessException: No class model.FileDB entity with id
undefined exists!
I am new to react / springboot and I cannot find the exact error please HELP.
fileDB MODEL
#Entity
#Table(name = "files")
public class FileDB {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
FileController.java
#DeleteMapping("/files/{id}")
public ResponseEntity<String> deleteFile(#PathVariable String id){
storageService.deleteFile(id);
return new ResponseEntity<>("file deleted", HttpStatus.OK);
}
FileService.java
public void deleteFile(String id) {
fileDBRepository.deleteById(id);
}
******react
export default class UploadFiles extends Component {
constructor(props) {
super(props);
this.selectFiles = this.selectFiles.bind(this);
this.upload = this.upload.bind(this);
this.uploadFiles = this.uploadFiles.bind(this);
this.state = {
selectedFiles: undefined,
progressInfos: [],
message: null,
fileInfos: [],
files:[]
};
}
componentDidMount() {
UploadService.getFiles().then((response) => {
this.setState({
fileInfos: response.data,
});
});
}
deleteFile=(fileId) =>{
axios.delete("http://localhost:8181/files/"+fileId)
.then(response => {
if(response.data !=null){
this.setState({
files:this.state.files.filter(file => file.id !== fileId)
});
}
});
};
render()
{
//code
<Table bordered hover striped variant="dark" >
<thead>
<tr>
<th> List of Files </th>
<th> Actions</th>
</tr>
</thead>
<tbody>
{
fileInfos &&
fileInfos.map((file, id) =>
<tr key = {id}>
<td> <a href={file.url}>{file.name}</a> </td>
<td>
<ButtonGroup>
<Button onClick={this.deleteFile.bind(this,id)}
size="sm" variant="outline-danger">
<FontAwesomeIcon icon={faTrash} />
</Button>
{' '}
<Button size="sm" variant="outline-danger">
<FontAwesomeIcon icon={faEye} />
</Button>
</ButtonGroup>
</td>
</tr>
)
}
</tbody>
</Table>
}
Your Java code APi it's working properly, i thin the problem is in your client side, if the response of UploadService.getFiles() is something like this:
{
....
data:[
{
"id": "afd564",
"name": "hello",
"link": "this the link"
}
]
....
}
export default class UploadFiles extends Component {
constructor(props) {
super(props);
this.selectFiles = this.selectFiles.bind(this);
this.upload = this.upload.bind(this);
this.uploadFiles = this.uploadFiles.bind(this);
this.state = {
selectedFiles: undefined,
progressInfos: [],
message: null,
fileInfos: [],
files:[]
};
}
componentDidMount() {
UploadService.getFiles().then((response) => {
this.setState({
fileInfos: response.data,
});
});
}
deleteFile=(fileId) =>{
axios.delete("http://localhost:8181/files/"+fileId)
.then(response => {
if(response.data !=null){
this.setState({
files:this.state.files.filter(file => file.id !== fileId)
});
}
});
};
render()
{
//code
<Table bordered hover striped variant="dark" >
<thead>
<tr>
<th> List of Files </th>
<th> Actions</th>
</tr>
</thead>
<tbody>
{
fileInfos &&
fileInfos.map((file, id) =>
<tr key = {id}>
<td> <a href={file.url}>{file.name}</a> </td>
<td>
<ButtonGroup>
<Button onClick={this.deleteFile.bind(this,file.id)}
size="sm" variant="outline-danger">
<FontAwesomeIcon icon={faTrash} />
</Button>
{' '}
<Button size="sm" variant="outline-danger">
<FontAwesomeIcon icon={faEye} />
</Button>
</ButtonGroup>
</td>
</tr>
)
}
</tbody>
</Table>
}

How to map JSON data as a table in React

I'm trying to display data after fetching it, but that does not work :
import React, { Component } from "react";
import { Table, Button, ButtonToolbar } from "react-bootstrap";
const URL = "http://localhost:51644/api/";
let passedAthleteId = 0;
let passedAthleteSessionId = 0;
class AthleteTrainingSession extends Component {
constructor(props) {
super(props);
this.state = {
athleteTrainingSession: [],
discussions: [],
warmups: [],
workouts: [],
stretchings: [],
};
}
componentDidMount() {
this.fetchAthleteTrainingSession();
}
componentDidUpdate() {
this.fetchAthleteTrainingSession();
}
fetchAthleteTrainingSession = () => {
fetch(URL + `Coaches/4/Athletes/1/AthleteSessions/4`)
.then((response) => response.json())
.then((data) => {
this.setState({
athleteTrainingSession: data,
});
});
};
render() {
const {
athleteTrainingSession,
discussions,
warmups,
workouts,
stretchings,
} = this.state;
passedAthleteId = this.props.match.params.athleteId;
passedAthleteSessionId = this.props.match.params.athleteSessionId;
this.discussions = this.state.athleteTrainingSession.Discussions;
this.warmups = this.state.athleteTrainingSession.Warmups;
this.workouts = this.state.athleteTrainingSession.Workouts;
this.stretchings = this.state.athleteTrainingSession.Stretchings;
console.log(athleteTrainingSession);
console.log(this.warmups);
return (
<React.Fragment>
<div>
<h2 className="mt-2">
Programme d'entraînement :{" "}
{athleteTrainingSession.TrainingProgramName}
</h2>
<h4>
Séance d'entraînement : {athleteTrainingSession.TrainingSessionName}
</h4>
</div>
<div>
<ButtonToolbar>
<Button variant="primary">Ajouter</Button>
<Button variant="secondary">Discussion</Button>
</ButtonToolbar>
<h4>Échauffement</h4>
<Table className="mt-4" striped bordered hover size="sm">
<thead>
<tr className="d-flex">
<th className="col-6">Exercice</th>
<th className="col-6">Options</th>
</tr>
</thead>
<tbody>
{warmups.map((warm) => (
<tr className="d-flex" key={warm}>
<td className="col-6">{warm.ExerciseName}</td>
<td className="col-6">
<ButtonToolbar>
<Button className="mr-2" variant="info">
Modifier
</Button>
<Button className="mr-2" variant="danger">
Supprimer
</Button>
</ButtonToolbar>
</td>
</tr>
))}
</tbody>
</Table>
</div>
</React.Fragment>
);
}
}
export default AthleteTrainingSession;
athleteTrainingSession contains the fetched data, and warmups is a sub-object for athleteTrainingSession.
When I console.log(warmups), I can see that it does contain data, but I cannot display it in the table.
athleteTrainingSession contains the fetched data, and warmups is a sub-object for athleteTrainingSession.
When I console.log(warmups), I can see that it does contain data, but I cannot display it in the table.
I think you have misconception of using state in component.
You're able to console the warmups because in your code you console.log(this.warmups), but you render the map with this.state.warmups
you should setState all of the data that you get from fetch, i.e:
fetchAthleteTrainingSession = () => {
fetch(URL + `Coaches/4/Athletes/1/AthleteSessions/4`)
.then((response) => response.json())
.then((data) => {
this.setState({
athleteTrainingSession: data,
warmups: data.Warmups,
workouts: data.Workouts,
discussions: data.Discussions,
stretchings: data.Stretchings,
});
});
};
by doing this way, now you can access the warmups data from this.state.warmups then render it
render() {
const {
athleteTrainingSession,
discussions,
warmups,
workouts,
stretchings,
} = this.state;
return (
<React.Fragment>
...
{warmups.map((warm) => (
<tr className="d-flex" key={warm}>
<td className="col-6">{warm.ExerciseName}</td>
<td className="col-6">
<ButtonToolbar>
<Button className="mr-2" variant="info">
Modifier
</Button>
<Button className="mr-2" variant="danger">
Supprimer
</Button>
</ButtonToolbar>
</td>
</tr>
))}
...
</React.Fragment>
)
}

How can i simplify my current React code?

How can I simplify my React code? I have a feeling that I'm not writing DRY React code.
I have multiple handlers and setStates, and Sorting functions.. but can I make one function which handles one state.. and sort it from there? I will split my code in chunks and components.
What can i do to simplify my code? Any suggestion?
import React, { Component } from 'react';
import { getMovies } from '../services/fakeMovieService';
class Movies extends Component {
state = {
isSorted: false,
movies: getMovies(),
isDisabled: true,
};
componentDidMount() {
console.log(this.state.movies);
}
// Display all movies but not the one selected.
handleDelete = movie => {
this.setState({
movies: this.state.movies.filter(m => m._id !== movie._id),
isDisabled: false,
});
console.log(`Movie ${movie.title} deleted.`);
};
// Sort all video's by title
sortByTitle = () => {
this.setState({
movies: this.state.movies.sort((a, b) =>
a.title.localeCompare(b.title)
),
isDisabled: false,
});
console.log('sorted by title');
};
// Sort all video's by genre
sortByGenre = () => {
this.setState({
movies: this.state.movies.sort((a, b) =>
a.genre.name.localeCompare(b.genre.name)
),
isDisabled: false,
});
console.log('sorted by genre.');
};
// sort all video's by stock size
sortByStock = () => {
this.setState({
movies: this.state.movies.sort(
(a, b) => a.numberInStock - b.numberInStock
),
isDisabled: false,
});
console.log('sorted by stock size.');
};
// Sort all video's by rating score
sortByRating = () => {
this.setState({
movies: this.state.movies.sort(
(a, b) => a.dailyRentalRate - b.dailyRentalRate
),
isDisabled: false,
});
console.log('sorted by rating sore.');
};
// Add class to the reset button based on the current filter state.
resetButtonClass = () => {
let btnClass = 'btn btn-sm btn-';
btnClass += this.state.isDisabled ? 'warning' : 'primary';
return btnClass;
};
// Reset the video's filter
resetFilter = () => {
this.setState({
movies: [...getMovies()],
isDisabled: true,
});
console.log('Reset movies database.');
};
render() {
const { length: count } = this.state.movies;
// Check if there are movies available.
if (count === 0)
return (
<React.Fragment>
<h4>There are no movies in the database.</h4>
<button
type="button"
onClick={this.resetFilter}
className="btn btn-primary btn-sm">
Reset
</button>
</React.Fragment>
);
return (
<React.Fragment>
<h5 className="pb-2">Showing {count} in the database.</h5>
<table className="table table-hover table-striped table-dark">
<thead>
<tr>
<th>
<span onClick={this.sortByTitle}>Title</span>
</th>
<th>
<span onClick={this.sortByGenre}>Genre</span>
</th>
<th>
<span onClick={this.sortByStock}>Stock</span>
</th>
<th>
<span onClick={this.sortByRating}>Rate</span>
</th>
<th>
<button
onClick={this.resetFilter}
className={this.resetButtonClass()}
disabled={this.state.isDisabled && true}>
Reset filter
</button>
</th>
</tr>
</thead>
<tbody>
{this.state.movies.map(movie => {
const {
_id,
title,
genre,
numberInStock,
dailyRentalRate,
} = movie;
return (
<tr key={_id}>
<th>{title}</th>
<td>{genre.name}</td>
<td>{numberInStock}</td>
<td>{dailyRentalRate}</td>
<td>
<button
onClick={() =>
this.handleDelete(movie)
}
className="btn btn-danger btn-sm">
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</React.Fragment>
);
}
}
export default Movies;
You can make it much DRYer by using a more generalised sort function:
// general sort function
sortBy = predicateFunction => {
this.setState({
movies: this.state.movies.sort( predicateFunction ),
isDisabled: false,
});
};
// and then call it
sortByRating = () => this.sortBy((a, b) => a.dailyRentalRate - b.dailyRentalRate)
sortByTitle = () => this.sortBy((a, b) =>
a.title.localeCompare(b.title))
... etc

_deleteQuote is not a function

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} />

Resources