add a click event after deleting list - reactjs

i have created a fuction in which onClick i delete movie but I also want to add a add button by which clicking on add button i get my deleted movie back
here is my code
class Movies extends Component {
state = {
movies: getMovies(),
};
handleDelete=(movi)=>{
const movies = this.state.movies.filter(m=> m._id !== movi._id)
this.setState({movies})
}
render() {
return (
<table className="table">
<thead>
<tr>
<th>Title</th>
<th>Genre</th>
<th>Stock</th>
<th>Rate</th>
<th></th>
</tr>
</thead>
<tbody>
{this.state.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 onClick={()=>this.handleDelete(movie)} className="btn btn-danger btn-outline-warning btn-sm active ">Remove</td>
</tr>
))}
</tbody>
</table>
);
}
}

You need to somehow keep track of the movies that you are deleting so that you can reinstate them... 3 Components (App - parent, Movie & Deleted)
Here is your App:
export default class App extends Component {
state = { movies: getMovies(), deleted: [] };
handleDelete = id => {
const movie = this.state.movies.find(movie => movie.id === id);
this.setState({ deleted: [...this.state.deleted, movie] });
this.setState({
movies: this.state.movies.filter(movie => movie.id !== id)
});
};
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)
});
};
render() {
return (
<div>
<h1>Movies</h1>
{this.state.movies.map(movie => {
return (
<Movie
key={movie.id}
movie={movie}
handleDelete={this.handleDelete}
/>
);
})}
<hr />
<h1>Deleted</h1>
{this.state.deleted.map(movie => {
return (
<Deleted
key={movie.id}
movie={movie}
handleReinstate={this.handleReinstate}
/>
);
})}
</div>
);
}
}
Here is your Movie:
export default function Movie({ movie, handleDelete }) {
return (
<div>
<h4>{movie.title}</h4>
<button onClick={() => handleDelete(movie.id)}>Delete</button>
</div>
);
}
Here is your Deleted / Reinstate:
export default function Deleted({ movie, handleReinstate }) {
return (
<div>
<h4>{movie.title}</h4>
<button onClick={() => handleReinstate(movie.id)}>Reinstate</button>
</div>
)
}
Here is a link to a live demo: https://stackblitz.com/edit/react-umffju?file=src%2FApp.js

For this type of problem, here’s what you could do. Store the deleted movies in state with something like this:
This.state={
currentMovies: [list of movies],
deletedMovies: [list of movies]
}
Have an array that stores Json objects like your movies. Then have a function for storing movies that have been deleted:
const deleteMoviesArray=[]
deleteMovie(movieData){
deleteMoviesArray.push(movieData);
This.setState({
deletedMovies: deletedMoviesArray
})
};
Now you have stored your deleted movies. To retrieve your deleted movies, you might use a drop down box something like this:
<select>
{
This.state.deletedMovies.map(data =>(
<option> {movie.title}</option>
))
}
</select>
And once you select one, do a similar thing to what you did with deleted movies, add a movie back to an array of available movies.
If you need your list of deleted movies to persist past a page refresh, you might consider storing that data in session storage:
window.sessionStorage.setItem(‘deletedMovieArray’, [list of movie objects])
And get them
window.sessionStorage.getItem(‘deletedMovieArray’)

Related

How can I only manipulate only 1 item in the array map independently

I have created a table like this, and I created all of these buttons in the array map function
Whenever I click on the Edit button in every button, it'll display all of it at the same time
How can I press Edit, for example, Edit button on RoleID 1, it'll only display the Edit table there for me to edit, not all of it, I don't know how to separate it since it's stuck in the map array I've created.
Here is my code, I have shorten it for easier to read:
class RoleManagement extends Component {
constructor(props) {
super(props);
this.state = {
roleList: [],
showHideEdit: false,
};
this.toggleEdit = this.toggleEdit.bind(this);
}
async componentDidMount() {
await axios.get("http://localhost:3000/admin/get-role-list").then((res) => {
this.setState({
roleList: res.data,
});
});
}
/---This is the toggle to display edit
toggleEdit(name) {
switch (name) {
case "showHideEdit":
this.setState({ showHideEdit: !this.state.showHideEdit });
break;
default:
null;
}
}
render() {
const { showHideEdit } = this.state;
return (
<table>
<thead>
<tr>
<th>Role ID</th>
<th>Role Name</th>
<th>Action</th>
</tr>
</thead>
{this.state.roleList.map((element, i) => {
return (
<>
<tbody key={i}>
<tr>
<td>{element.role_ID}</td>
<td>{element.rolename}</td>
<td className="admin-button">
/---- The edit button is here
<button
className="admin-config-button"
onClick={() => this.toggleEdit("showHideEdit")}
>
Edit
</button>
{showHideEdit && <RoleEdit id={element.key} />}
</td>
</tr>
</tbody>
</>
);
})}
);
}
}
Because you are using one boolean to check for all RoleEdit. Show when showHideEdit is true, all RoleEdit will show.
To fix, you can update showHideEdit is index of item:
onClick={() => this.toggleEdit(i)}
toggleEdit(i){
if(this.state.showHideEdit === i) {
this.setState({ showHideEdit: null});
} else {
this.setState({ showHideEdit: i});
}
}
{showHideEdit === i && <RoleEdit id={element.key} />}

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

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

ReactJS: send object as parameter to function, the fields are undefined

I'm react beginner. strange behavior:
I send an objct as parameter to a function. the function get the object , but the function can access field of this object. the fields are undefined.
I send the object 'movie' to the handleClickDelete. see the console logs: the 'movie' object is correct. but the movie._id is undefined.
the component:
class MainList extends Component {
state = {
count: APIMovieService.getMovies().length,
movies: APIMovieService.getMovies()
};
handleClickDelete = movie => {
console.log("delete movie", movie);
console.log("delete movie Id", movie._id);
const updateList = this.state.movies.filter(
iMove => iMove._id === movie._id
);
// APIMovieService.deleteMovie(movie._id);
console.log("updateList: ", updateList);
this.updateState(updateList);
};
updateState = updateList => {
this.setState({ movies: updateList });
this.setState({ count: updateList.length });
};
render() {
return (
<div>
{this.handleCounter()}
{this.test1()}
<table className="table">
<thead>
<tr>
<th scope="col">title</th>
<th scope="col">genre</th>
<th scope="col">in stock</th>
<th scope="col">rate</th>
<th scope="col">delete</th>
</tr>
</thead>
<tbody>
{this.state.movies.map(movie => (
<tr key={movie._id}>
<td scope="row">{movie.title}</td>
<td>{movie.genre.name}</td>
<td>{movie.numberInStock}</td>
<td>{movie.dailyRentalRate}</td>
<td>
<button
className="btn btn-danger"
onClick={() => {
this.handleClickDelete({ movie });
}}
>
delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
handleCounter() {
return this.state.count === 0
? "the list is empty"
: this.state.count + " in the list";
}
export default MainList;
the log:
see picture.enter image description here
You are putting the movie inside an object movie.
For access the properties you need to access another movie property before, like this.
movie.movie.__id
To avois this behavior you need to change this:
onClick={() => {
this.handleClickDelete({ movie });
}}
For this
onClick={() => {
this.handleClickDelete(movie);
}}
You are passing the movie as an object. You need to pass it as a variable like:
<button
className="btn btn-danger"
onClick={() => this.handleClickDelete(movie)}
>
delete
</button>
Now you can access it in your handleClickDelete
const handleClickDelete = movie => {
console.log("delete movie", movie);
console.log("delete movie Id", movie._id);
}
Hope this works for you.
Change :
onClick={() => {
this.handleClickDelete({ movie });
}}
To :
onClick={() => {
this.handleClickDelete(movie)
}};```

Keys should be unique so that components maintain their identity across updates

I have A react component which renders a list of items that have been called from an API and set to setOfAllBooks state. Any time I search for the item, setOfAllBooks state is filtered through by the search ternm and the results are held in searchedBooks state. The results of searchedBooks are then passed to Table component and rendered in a list. At this point it works correctly, but when I search for another item it gets clustered in the Table. What I want to do is anytime I search a new Item after I have searched for a previos term I want the list-items in the Table component to be cleared to make way for the new items that have been searched.
import React, { Component } from 'react';
import './Home.css'
import axios from 'axios';
import Autosuggest from 'react-autosuggest';
var books = []
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : books.filter(book =>
book.title.toLowerCase().slice(0, inputLength) === inputValue);
};
const getSuggestionValue = suggestion => suggestion.title;
const renderSuggestion = suggestion => (
<div>
{suggestion.title}
</div>
);
const Table = ({ data }) => (
<table class="table table-hover">
<thead>
<tr class="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row =>
<TableRow row={row} />
)}
</tbody>
</table>
)
const TableRow = ({ row }) => (
<tr class="table-light">
<th scope="row" key={row.title}>{row.title}</th>
<td key={row.author}>{row.author}</td>
<td key={row.isbn}>{row.isbn}</td>
<td key={row.isbn}>24</td>
</tr>
)
class Home extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: []
};
this.searchBook = this.searchBook.bind(this);
}
componentDidMount(){
axios.get('/api/book/viewAll')
.then(res => {
this.setState({ setOfAllBooks: res.data });
books = this.state.setOfAllBooks;
console.log(this.state.setOfAllBooks)
})
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
}
searchBook(event){
event.preventDefault();
this.setState({value: this.state.value});
this.state.searchedBooks = this.state.setOfAllBooks.filter(book => book.title == this.state.value);
this.setState({searchedBook: []})
console.log(this.state.searchedBook);
}
render() {
const { value, suggestions } = this.state;
const inputProps = {
placeholder: 'Enter the name of the book',
value,
onChange: this.onChange
}
return (
<div class="form-group col-lg-4">
<label for="exampleInputEmail1">Email address</label>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
id="searchFor"
/>
<div className=" form-group">
<label htmlFor="searchFor"> </label>
<button class="form-control btn btn-success" type="submit" onClick={this.searchBook}>Search</button>
</div>
<Table data={this.state.searchedBooks} />
</div>
)
}
}
export default Home;
The results
The Error
You need to add the key prop to the TableRow component as <TableRow key={row.title} row={row} />. Remove the key where you have right now.
.... A good rule of thumb is that elements inside the map() call need keys.
... keys used within arrays should be unique among their siblings. . Doc.
So, it seems title what you used for key will still throw warnings, as they are not uniqe. If you have ID attribute in the row object use that. Adding key to TableRow will remove the first warning, but other warning still be there until title doesn't have the uniq values across all the data.

Resources