I keep getting error "Warning: Each child in a list should have a unique "key" prop" on console log. I think there is something wrong in the
axios.delete('http://localhost:3000/contacts/${id}
' or
axios.put('http://localhost:3000/contacts/${isUpdate.id}'
It works, but does not configure in the json-server that I made. So the changes does not happen in the json-server.
The "save" button works, and added new data into my json-server. But, when I tried to edit, or delete, it does not save the changed in json-server.
My code:
import "./App.css";
import List from "./List";
import { useState,useEffect } from "react";
import {uid} from "uid";
import axios from "axios";
function App() {
const [contacts, setContacts] = useState([]);
function handleChange(e) {
let data = { ...formData };
data[e.target.name] = e.target.value;
setFormData(data);
}
const [isUpdate, setIsUpdate] = useState({ id: null, status: false});
const [formData, setFormData] = useState({
name: "",
telp: "",
});
useEffect(() => {
axios.get("http://localhost:3000/contacts").then((res) => {
console.log(res.data);
setContacts(res?.data ?? []);
});
}, []);
function handleSubmit(e){
e.preventDefault();
alert("Oke Bos!");
let data = [...contacts];
if(formData.name === "") {
return false;
}
if(formData.telp === "") {
return false;
}
if ( isUpdate.status){
data.forEach((contact) => {
if (contact.id === isUpdate.id) {
contact.name = formData.name;
contact.telp = formData.telp;
}
});
axios.put('http://localhost:3000/contacts/${isUpdate.id}', {
name: formData.name,
telp: formData.telp,
})
.then((res) => {
alert("Berhasil edit data!");
});
} else {
let newData = { id: uid(), name: formData.name, telp: formData.telp };
data.push(newData);
axios.post("http://localhost:3000/contacts", newData).then((res) => {
alert("Data telah disimpan cok!");
});
}
// tambah kontak yee
setIsUpdate({id: null, status: false});
setContacts(data);
setFormData({ name: "", telp: ""});
}
function handleEdit(id) {
let data = [...contacts];
let foundData = data.find((contact) => contact.id === id);
setFormData({ name: foundData.name, telp: foundData.telp});
setIsUpdate({id: id, status: true});
}
function handleDelete(id) {
let data = [...contacts];
let filteredData = data.filter((contact) => contact.id !== id);
axios.delete('http://localhost:3000/contacts/${id}').then((res) => {
alert("Data telah dihapus");
});
setContacts(filteredData);
}
return (
<div className="App">
<h1 className="px-3 py-3">My Contact List</h1>
<form onSubmit={handleSubmit} className="px-3 py-4">
<div className="form-group">
<label htmlFor="">Name</label>
<input type="text"
className="form-control"
onChange={handleChange}
value={formData.name}
name="name" />
</div>
<div className="form-group mt-3">
<label htmlFor="">No. Telp</label>
<input type="text"
className="form-control"
onChange={handleChange}
value={formData.telp}
name="telp" />
</div>
<div>
<button type="submit" className="btn btn-primary w-100 mt-3">
Save
</button>
</div>
</form>
<List handleDelete={handleDelete} handleEdit={handleEdit} data={contacts} />
</div>
);
}
export default App;
The list component:
import React from "react";
export default function List({data,handleEdit,handleDelete}) {
return (
<div className="list-group">
{
data.map((contact) => {
return (
<div className="list-group-item list-group-item-action">
<div className="d-flex w-100 justify-content-between">
<h5 className="mb-1">{contact.name}</h5>
<div>
<button onClick={() => handleEdit(contact.id)} className="btn btn-sm btn-link">Edit</button>
<button onClick={() => handleDelete(contact.id)} className="btn btn-sm btn-link">Del</button>
</div>
</div>
<p className="mb-1">{contact.telp}</p>
</div>
);
})}
</div>
);
}
I follow the tutorial, the code is like this, i checked it in so many times. But still it does not configure well into json-server like what i see on youtube.
I was doing a Node - React course in Coursera, the course is really outdated so i decided to follow the course but update the code to current versions of frameworks and technologies. The project was to build up a restaurant web app, all the app is working well but the part showing the menu is supposed to render a specific dish when clicked, but when done, the app crashes showing the following error. The code of the route in the server is:
dishRouter.js
const bodyParser = require('body-parser');
const express = require('express');
const cors = require('./cors');
const authenticate = require('../authenticate');
const Dishes = require('../models/dishes');
.
.
.
dishRouter.route('/:dishId')
.options(
cors.corsWithOptions,
(req, res) => {
res.sendStatus(200);
}
)
.get(
cors.cors,
(req, res, next) => {
Dishes.findById(req.params.dishId)
.populate('comments.author')
.then((dish) => {
console.log('Dish Created ', dish);
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(dish);
}, (err) => next(err))
.catch((err) => next(err));
}
)
.post(
cors.corsWithOptions,
authenticate.verifyUser,
authenticate.verifyAdmin,
(req, res, _next) => {
res.statusCode = 403;
res.end(`POST operation not supported on /dishes/${req.params.dishId}`);
}
)
.put(
cors.corsWithOptions,
authenticate.verifyUser,
authenticate.verifyAdmin,
(req, res, next) => {
Dishes.findByIdAndUpdate(req.params.dishId, {
$set: req.body
}, { new: true })
.then((dish) => {
console.log('Dish Created ', dish);
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(dish);
}, (err) => next(err))
.catch((err) => next(err));
}
)
.delete(
cors.corsWithOptions,
authenticate.verifyUser,
authenticate.verifyAdmin,
(req, res, next) => {
Dishes.findByIdAndRemove(req.params.dishId)
.then((resp) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(resp);
}, (err) => next(err))
.catch((err) => next(err));
}
);
module.exports = dishRouter;
the router dishRouter.route('/:dishId') is the responsible to handle the specific dish information
The client side code
mainComponent.js
import React, { Component } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from './ContactComponent';
import DishDetail from './DishdetailComponent';
import Favorites from './FavoriteComponent';
import Header from './HeaderComponent';
import Footer from './FooterComponent';
import About from './AboutComponent'
import {Routes, Route, Navigate, useParams, useNavigate, useLocation} from 'react-router-dom'; //Switch changed to routes Also redirect is changed to Navigate since version 6
import {connect} from 'react-redux';
import { postComment, postFeedback,fetchDishes, fetchComments, fetchPromos, fetchLeaders,loginUser, logoutUser, fetchFavorites, postFavorite, deleteFavorite } from '../redux/ActionCreators';
import { actions } from 'react-redux-form';
import {TransitionGroup, CSSTransition} from 'react-transition-group';
// --------Hook to use withRouter from v5 in actual v6
export const withRouter = (Component) => {
const Wrapper = (props) => {
const history = useNavigate();
return (
<Component
history={history}
{...props}
/>
);
};
return Wrapper;
};
//------------End of Hook
const mapStateToProps = (state) => {
return{
dishes: state.dishes,
comments: state.comments,
promotions: state.promotions,
leaders: state.leaders,
favorites: state.favorites,
auth: state.auth
}
}
const mapDispatchToProps = dispatch => ({
postComment: (dishId, rating, author, comment) => dispatch(postComment(dishId, rating, author, comment)),
postFeedback: (firstName, lastName, telnum, email, agree, contactType, message) => dispatch(postFeedback(firstName, lastName, telnum, email, agree, contactType, message)),
fetchDishes: () => { dispatch(fetchDishes())},
fetchComments: () => dispatch(fetchComments()),
fetchPromos: () => dispatch(fetchPromos()),
fetchLeaders: () => dispatch(fetchLeaders()),
resetFeedbackForm: () => { dispatch(actions.reset('feedback'))},
loginUser: (creds) => dispatch(loginUser(creds)),
logoutUser: () => dispatch(logoutUser()),
fetchFavorites: () => dispatch(fetchFavorites()),
postFavorite: (dishId) => dispatch(postFavorite(dishId)),
deleteFavorite: (dishId) => dispatch(deleteFavorite(dishId))
});
class Main extends Component {
componentDidMount() {
this.props.fetchDishes();
this.props.fetchComments();
this.props.fetchPromos();
this.props.fetchLeaders();
this.props.fetchFavorites();
}
render(){
const HomePage = () => {
// console.log(this.props)
return(
<Home
dish={this.props.dishes.dishes.filter((dish) => dish.featured)[0]}
dishesLoading={this.props.dishes.isLoading}
dishesErrMess={this.props.dishes.errMess}
promotion={this.props.promotions.promotions.filter((promo) => promo.featured)[0]}
promoLoading={this.props.promotions.isLoading}
promoErrMess={this.props.promotions.errMess}
leader={this.props.leaders.leaders.filter((leader) => leader.featured)[0]}
leaderLoading={this.props.leaders.isLoading}
leaderErrMess={this.props.leaders.errMess}
/>
);
}
const DishWithId = () => {
let params = useParams();
return(
this.props.auth.isAuthenticated
?
<DishDetail dish={this.props.dishes.dishes.filter((dish) => dish._id === parseInt(params.dishId,10))[0]}
isLoading={this.props.dishes.isLoading}
errMess={this.props.dishes.errMess}
comments={this.props.comments.comments.filter((comment) => comment.dishId === parseInt(params.dishId,10))}
commentsErrMess={this.props.comments.errMess}
postComment={this.props.postComment}
favorite={this.props.favorites.favorites.dishes.some((dish) => dish._id === params.dishId)}
postFavorite={this.props.postFavorite}
/>
:
<DishDetail dish={this.props.dishes.dishes.filter((dish) => dish._id === parseInt(params.dishId,10))[0]}
isLoading={this.props.dishes.isLoading}
errMess={this.props.dishes.errMess}
comments={this.props.comments.comments.filter((comment) => comment.dishId === parseInt(params.dishId,10))}
commentsErrMess={this.props.comments.errMess}
postComment={this.props.postComment}
favorite={false}
postFavorite={this.props.postFavorite}
/>
);
};
const AnimatedSwitch = () => {
const location = useLocation();
console.log("location", location);
console.log("Props", this.props);
return (
<TransitionGroup>
<CSSTransition key ={location.key} classNames='page' timeout = {300}>
<Routes>
<Route path="/home" element={<HomePage/>}/> {/*Also component must be changed to element since i am using V6*/}
<Route exact path="/menu" element= {<Menu dishes={this.props.dishes} />} /> {/* in previous version this must look as: () => element= {<Menu dishes={this.props.dishes} />} */}
<Route path = "/menu/:dishId" element={<DishWithId />} />
{/* <PrivateRoute exact path="/favorites" component={() => <Favorites favorites={this.props.favorites} deleteFavorite={this.props.deleteFavorite} />} /> */}
<Route exact path ='/contactus' element={<Contact postFeedback={this.props.postFeedback} resetFeedbackForm={this.props.resetFeedbackForm}/>} />
<Route path = '/aboutus' element={<About leaders = {this.props.leaders}/>} />
<Route path="*"element={<Navigate to="/home" />} />
{/* Instead of redirect the above snippet is needed to redirect if there is no matched url */}
</Routes>
</CSSTransition>
</TransitionGroup>
);
};
return (
<div>
<Header
auth={this.props.auth}
loginUser={this.props.loginUser}
logoutUser={this.props.logoutUser} />
<AnimatedSwitch/>
<Footer/>
</div>
);
}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
menuComponent.js
import React from 'react';
import { Card, CardImg, CardImgOverlay,CardTitle,Breadcrumb, BreadcrumbItem} from 'reactstrap';
import {Link} from 'react-router-dom';
import { Loading } from './LoadingComponent';
import {baseUrl} from '../shared/baseUrl';
function RenderMenuItem ({dish, onClick}) {
return (
<Card>
<Link to={ `/menu/${dish._id}` } >
<CardImg width="100%" src={baseUrl + dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle>{dish.name}</CardTitle>
</CardImgOverlay>
</Link>
</Card>
);
}
const Menu = (props) =>{
const menu = props.dishes.dishes.map((dish) => { // antes era this.props.dishes pero ahora props pasa como parametro a la funcion por lo cual this no es necesario
return (
<div key={dish._id} id = 'dishes' className="col-12 col-md-5 m-1">
<RenderMenuItem dish={dish}/>
</div>
);
});
if (props.dishes.isLoading){
return(
<div className="container">
<div className="row">
<Loading />
</div>
</div>
);
}
else if(props.dishes.errMess){
return(
<div className="container">
<div className="row">
<h4>{props.dishes.errMess}</h4>
</div>
</div>
);
}
else
return (
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem><Link to="/home">Home</Link></BreadcrumbItem>
<BreadcrumbItem active>Menu</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>Menu</h3>
<hr />
</div>
</div>
<div className="row">
{menu}
</div>
</div>
);
}
export default Menu;
dishdetailComponent.js
import React, {Component} from 'react';
import { Card, CardImg,CardImgOverlay, CardText, CardBody, CardTitle,
Breadcrumb,BreadcrumbItem, Button,
Modal, ModalHeader, ModalBody,
Label, Col, Row} from 'reactstrap';
import {Link} from 'react-router-dom'
import { Control, LocalForm, Errors } from 'react-redux-form';
import { Loading } from './LoadingComponent';
import { baseUrl } from '../shared/baseUrl';
import {FadeTransform, Fade, Stagger} from 'react-animation-components';
/**........................ comment component ends ................................................. */
//// validators
const required = (val) => val && val.length; //value > 0
const maxLength = (len) => (val) => !(val) || (val.length <= len);
const minLength = (len) => (val) => (val) && (val.length >= len);
class CommentForm extends Component {
constructor(props) {
super(props);
this.state = {
isCommentFormModalOpen: false
};
this.toggleCommentFormModal = this.toggleCommentFormModal.bind(this);
this.handleCommentFormSubmit = this.handleCommentFormSubmit.bind(this);
}
handleCommentFormSubmit(values) {
this.props.postComment(this.props.dishId, values.rating, values.author, values.comment);
}
toggleCommentFormModal() {
this.setState({
isCommentFormModalOpen: !this.state.isCommentFormModalOpen
});
}
render() {
return (
<React.Fragment>
<Button outline onClick={this.toggleCommentFormModal}>
<span className="fa fa-comments fa-lg"></span> Submit Comment
</Button>
{/* commentform Modal */}
<Modal isOpen={this.state.isCommentFormModalOpen} toggle={this.toggleCommentFormModal} >
<ModalHeader toggle={this.toggleCommentFormModal}> Submit Comment </ModalHeader>
<ModalBody>
<LocalForm onSubmit={(values) => this.handleCommentFormSubmit(values)}>
{/* rating */}
<Row className="form-group">
<Label htmlFor="rating" md={12} >Rating</Label>
<Col md={12}>
<Control.select model=".rating"
className="form-control"
name="rating"
id="rating"
validators={{
required
}}
>
<option>Please Select</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Control.select>
<Errors
className="text-danger"
model=".author"
show="touched"
messages={{
required: 'Required',
}}
/>
</Col>
</Row>
{/* author */}
<Row className="form-group">
<Label htmlFor="author" md={12}> Your Name </Label>
<Col md={12}>
<Control.text model=".author" id="author" name="author"
placeholder="First Name"
className="form-control"
validators={{
required, minLength: minLength(3), maxLength: maxLength(15)
}}
/>
<Errors
className="text-danger"
model=".author"
show="touched"
messages={{
required: 'Required',
minLength: 'Must be greater than 2 characters',
maxLength: 'Must be 15 characters or less'
}}
/>
</Col>
</Row>
{/* comment */}
<Row className="form-group">
<Label htmlFor="comment" md={12}>Comment</Label>
<Col md={12}>
<Control.textarea model=".comment" id="comment" name="comment"
rows="6"
className="form-control"
validators={{
required
}}
/>
<Errors
className="text-danger"
model=".author"
show="touched"
messages={{
required: 'Required',
}}
/>
</Col>
</Row>
{/* submit button */}
<Row className="form-group">
<Col>
<Button type="submit" color="primary">
Submit
</Button>
</Col>
</Row>
</LocalForm>
</ModalBody>
</Modal>
</React.Fragment>
);
}
}
/**........................ comment component ends ................................................. */
function RenderDish({dish,favorite, postFavorite}){
if (dish != null){
return(
<div className='col-12 col-md-5 m-1'>
<FadeTransform in
transformProps={{
exitTransform: 'scale(0.5) translateY(-50%)'
}}>
<Card>
<CardImg width="100%" src={baseUrl + dish.image} alt={dish.name}/>
<CardImgOverlay>
<Button outline color="primary" onClick={() => favorite ? console.log('Already favorite') : postFavorite(dish._id)}>
{favorite ?
<span className="fa fa-heart"></span>
:
<span className="fa fa-heart-o"></span>
}
</Button>
</CardImgOverlay>
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
</FadeTransform>
</div>
);
}
else{
return(
<div></div>
);
}
}
function RenderComments({comments,postComment,dishId}){
if(comments != null){
const review = comments.map((comment) =>{
return (
<Stagger in >
<div key = {comment._id} className='container'>
<Fade in>
<li id='comments'>
<p>{comment.comment}</p>
<p>--{comment.author.firstname} {comment.author.lastname} , {new Intl.DateTimeFormat('en-US',{year: 'numeric', month:'short', day:'2-digit'}).format(new Date(Date.parse(comment.date)))}</p>
</li>
</Fade>
</div>
</Stagger>
);
});
return(
<div className='col-12 col-md-5 m-1'>
{review}
<CommentForm dishId={dishId} postComment={postComment} />
</div>
)
}
}
const DishDetail = (props)=> {
if (props.isLoading){
return(
<div className="container">
<div className="row">
<Loading />
</div>
</div>
);
}
else if(props.errMess){
return(
<div className="container">
<div className="row">
<h4>{props.errMess}</h4>
</div>
</div>
);
}
else if (props.dish != null){
return (
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem><Link to="/menu">Menu</Link></BreadcrumbItem>
<BreadcrumbItem active>{props.dish.name}</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>{props.dish.name}</h3>
<hr />
</div>
</div>
<div className="row">
<RenderDish dish={props.dish} favorite={props.favorite} postFavorite={props.postFavorite}/>
<RenderComments comments={props.comments}
postComment={props.postComment}
dishId={props.dish._id} />
</div>
</div>
);
}
else{
return(
<div></div>
);
}
}
export default DishDetail;
Also the favorites is a feature to add favorites dishes to a user.
from client side.:
favoriteComponent.js
import React, { Component } from 'react';
import { Media, Breadcrumb, BreadcrumbItem, Button } from 'reactstrap';
import { Link } from 'react-router-dom';
import { baseUrl } from '../shared/baseUrl';
import { Loading } from './LoadingComponent';
function RenderMenuItem({ dish, deleteFavorite }) {
return(
<Media tag="li">
<Media left middle>
<Media object src={baseUrl + dish.image} alt={dish.name} />
</Media>
<Media body className="ml-5">
<Media heading>{dish.name}</Media>
<p>{dish.description}</p>
<Button outline color="danger" onClick={() => deleteFavorite(dish._id)}>
<span className="fa fa-times"></span>
</Button>
</Media>
</Media>
);
}
const Favorites = (props) => {
if (props.favorites.isLoading) {
return(
<div className="container">
<div className="row">
<Loading />
</div>
</div>
);
}
else if (props.favorites.errMess) {
return(
<div className="container">
<div className="row">
<h4>{props.favorites.errMess}</h4>
</div>
</div>
)
}
else if (props.favorites.favorites) {
const favorites = props.favorites.favorites.dishes.map((dish) => {
return (
<div key={dish._id} className="col-12 mt-5">
<RenderMenuItem dish={dish} deleteFavorite={props.deleteFavorite} />
</div>
);
});
return(
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem><Link to='/home'>Home</Link></BreadcrumbItem>
<BreadcrumbItem active>My Favorites</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>My Favorites</h3>
<hr />
</div>
</div>
<div className="row">
<Media list>
{favorites}
</Media>
</div>
</div>
);
}
else {
return(
<div className="container">
<div className="row">
<h4>You have no favorites</h4>
</div>
</div>
)
}
}
export default Favorites;
The error is obvious
cannot access property "dishes", this.props.favorites.favorites is null
for some reason the favorites object is null and you cannot access the property dishes of the null object
favorite={this.props.favorites.favorites?.dishes?.some((dish) => dish._id === params.dishId)}
I wanna add the pin feature. When a note is pinned the icon color will be green and when it's not pinned, the icon should be black. when I click the pin button, the database is updating but the color is not changing. The color is changing when I reload the page.
I was trying but I couldn't.
This is the code of the child component
import React, { useState } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faPenToSquare, faTrash, faExpand, faThumbTack } from '#fortawesome/free-solid-svg-icons';
import { Link } from 'react-router-dom';
import axios from 'axios';
const SingleNote = ({ note, pinned, setPinned }) => {
const { title, body, _id, pinStatus } = note;
const handlePin = () => {
pinStatus ? axios.put(`http://localhost:5000/notes/pin/${_id}`, { pinStatus: false })
.then(res => {
res.status === 200 && setPinned(false)
})
:
axios.put(`http://localhost:5000/notes/pin/${_id}`, { pinStatus: true })
.then(res => {
res.status === 200 && setPinned(true)
})
}
return (
<div className="rounded bg-white overflow-hidden transition-all duration-300 shadow-lg hover:shadow-purple-100">
<div className="px-6 py-4 relative">
<p className="font-bold text-xl mb-2 font-serif">
{
title.length > 19 ? `${title.slice(0, 20)}...` : title
}
</p>
<button className='absolute top-3 right-3' onClick={handlePin}>
<FontAwesomeIcon
icon={faThumbTack}
className={
pinStatus ? 'text-emerald-500' : 'text-black'
}
/>
</button>
<hr />
<p className="text-gray-700 text-base">
{
body.length > 99 ? `${body.slice(0, 100)}...` : body
}
</p>
</div>
<div className="px-6 pt-4 pb-2 flex justify-end">
<Link to={`/notes/${_id}`}>
<button className='btn-c btn'>
<FontAwesomeIcon icon={faExpand} />
</button>
</Link>
<Link to={`/edit/${_id}`}>
<button className='btn-b btn'>
<FontAwesomeIcon icon={faPenToSquare} />
</button>
</Link>
<button className='btn-a btn'>
<FontAwesomeIcon icon={faTrash} />
</button>
</div>
</div>
);
};
export default SingleNote;
This is the parent component
const AllNotes = () => {
const [pinned, setPinned] = useState(false);
const [notes, setNotes] = useState([])
useEffect(() => {
fetch(`http://localhost:5000/notes/`)
.then(res => res.json())
.then(data => {
setNotes(data)
})
}, [pinned])
return (
<>
<Header />
<div className='container mx-auto'>
<div className='p-10 grid grid-cols-1 sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5'>
{
notes.map((note) => <SingleNote
key={note._id}
note={note}
pinned={pinned}
setPinned={setPinned}
/>)
}
</div>
<Link to='/addNote'>
<button
className='fixed right-5 bottom-5 md:right-10 md:bottom-10 h-10 w-10 sm:w-12 sm:h-12 bg-emerald-600 hover:bg-emerald-700 transition-all duration-300 text-white rounded-full'
>
<FontAwesomeIcon icon={faPlus} />
</button>
</Link>
</div>
</>
);
};
export default AllNotes;
I am working on a simple CRUD project with the MERN stack.
In the project, I'm trying to update the blog for the particular article. But it's not updating the blogs collection.
index.js
app.put('/updateblog/:id', async (req, res) => {
const newBlogTitle = req.body.newBlogTitle;
const newBlogAuthor = req.body.newBlogAuthor;
const newBlogContent = req.body.newBlogContent;
const {id} = req.params;
console.log(id);
try{
await Blog.findByIdAndUpdate( id , (err , updatedBlog) => {
updatedBlog.blogTitle = newBlogTitle;
updatedBlog.blogAuthor = newBlogAuthor;
updatedBlog.blogContent = newBlogContent;
updatedBlog.save();
res.send("updated");
})
}catch(error){
console.log(error);
}
})
Edit.js
Using aixos to get the post all the post details:
useEffect(() => {
axios.get("http://localhost:5000/blogList" )
.then((response) => {
setContent(response.data)
console.log(response.data)
})
}, [])
Update the data using put method
const log = async(e) => {
e.preventDefault();
if (editorRef.current) {
console.log(editorRef.current.getContent());
//setContent(editorRef.current.getContent());
setNewBlogContent(editorRef.current.getContent())
}
try{
await axios.put('http://localhost:5000/updateblog/' + id, {
newBlogTitle,
newBlogAuthor,
newBlogContent
}).then(response => {
console.log(response.data);
console.log('Blog successfully updated')
navigate('/blog')
}).catch((error) => {
console.log(error)
})
}catch(error){
console.log(error);
}
}
if (!content) return null;
Blog Section
<div className="container">
<div className='fw-bold text-center mt-3 fs-1'>Edit Content</div>
<form onSubmit={log}>
{content.filter(cnt => cnt._id === id )
.map(cDetails => (
<div key={cDetails._id}>
<div className='container'>
<div className='container bg-light mt-3 p-3'>
<label for="Blogtitle" className="form-label mt-3">Blog Title</label>
<input type='text' name="title" id="Blogtitle" className=' mt-3 form-control' required
value={newBlogTitle}
onChange={e => setNewBlogTitle(e.target.value)} placeholder={cDetails.blogTitle} />
<label for="BlogAuthor" className="form-label mt-3">Blog Title</label>
<input type='text' name="authorName" id="BlogAuthor" className=' mt-3 form-control'
value={newBlogAuthor} required
onChange={e => setNewBlogAuthor(e.target.value)} placeholder={cDetails.blogAuthor} />
{/* <div className=' bg-light mt-3 text-muted fst-italic text-end px-4'> - By {cDetails.blogAuthor}</div> */}
</div>
<div className='container mt-5'>
<Editor
onInit={(evt, editor) => {
setNewBlogContent(editor.getContent({format: 'html'}));
}}
initialValue={cDetails.blogContent}
onEditorChange={(newValue, editor) => {
setNewBlogContent(editor.getContent({format: 'html'}));
}}
/>
</div>
</div>
</div>
) )
}
<button type='submit' className="btn btn-primary mt-3 mx-4" >Update editor content</button>
</form>
</div>
</div>
The content state example output,
I am trying to create a to-do list app with a timestamp and strike-through the list item when the delete button is clicked, but when I click the delete button all the items get stricked-through.
I have looked everywhere but I cant get it to work for my code. I am able to extract unique id through delete button function but i cant understand how to use that id to strike only that id.
import "../App.css";
import moment from "moment";
const List = () => {
const [strike, setStrike] = useState(false);
const [addtime, setaddTime] = useState(moment().utcOffset("+05:30").format("YYYY-MM-DD HH:mm:ss ").toString());
const [store, setStore] = useState([]);
const [endTime, setendTime] = useState();
const [inputData, setInputData] = useState('');
const [items, setItems] = useState([]);
//to add an item
const addItem = () => {
if (!inputData) {
} else {
const allInputData = {id: new Date().getTime().toString(), name:inputData}
setItems([...items, allInputData]);
setInputData("");
setaddTime(
moment().utcOffset("+05:30").format("YYYY-MM-DD HH:mm:ss ").toString()
);
setStore([...store, addtime]);
}
};
// to delete an item
const deleteItem = (index) => {
const boolstrike = items.filter((elem) => {
return index == elem.id;
})
console.log(boolstrike);
setStrike(!!boolstrike);
};
//to remove time
const deleteTime = () => {
setendTime (moment().utcOffset("+05:30").format("YYYY-MM-DD HH:mm:ss ").toString());
}
// to remove all items
const removeAll = () => {
setItems([]);
setStore([]);
};
return (
<>
<div className="parent-div">
<div className="child-div">
<h3>TO DO LIST</h3>
<div className="addItems">
<input
type="text"
placeholder="what needs to be done?"
value={inputData}
onChange={(e) => setInputData(e.target.value)}
/>
<button
type="button"
onClick={addItem}
className="btn btn-secondary"
>
Create
</button>
</div>
<div id="wrapper">
<div id="left" className="showItems">
{items.map((elem) => {
return (
<div className="eachItem" key={elem.id}>
<h3
style={{
textDecoration: strike ? "line-through" : "none",
}}
>
{elem.name}
</h3>
<button
type="button"
onClick={() => deleteItem(elem.id)}
className="btn btn-danger btn-sm"
>
Delete
</button>
</div>
);
})}
</div>
<div id="right">
{store.map((elim, ind) => {
return (
<div className="eachdate" key={ind}>
<h6>{"Task Created at:" + elim} </h6>
<button
type="button"
onClick={() => deleteTime()}
className="btn btn-danger btn-sm"
>
Delete
</button>
</div>
);
})}
</div>
</div>
<div className="showItems">
<button onClick={removeAll} className="btn btn-danger">
Delete All
</button>
</div>
</div>
</div>
</>
);
};
export default List;```
You can try to add strike in the items obj and track that. in that way, if delete btn is clicked, you can mark that item as strikedout.
You can set strike while delete
const deleteItem = (index) => {
const updatedItems = items.filter((elem) => {
if (index == elem.id) {
elem.strike = true;
}
return elem;
});
setItems(updatedItems);
};
and based on this property class will be applied, only deleted items will strick out.
updated code:
import React, { useState } from 'react';
import './App.css';
import moment from 'moment';
const List = () => {
const [strike, setStrike] = useState(false);
const [addtime, setaddTime] = useState(
moment().utcOffset('+05:30').format('YYYY-MM-DD HH:mm:ss ').toString()
);
const [store, setStore] = useState([]);
const [endTime, setendTime] = useState();
const [inputData, setInputData] = useState('');
const [items, setItems] = useState([]);
//to add an item
const addItem = () => {
if (!inputData) {
} else {
const allInputData = {
id: new Date().getTime().toString(),
name: inputData,
};
setItems([...items, allInputData]);
setInputData('');
setaddTime(
moment().utcOffset('+05:30').format('YYYY-MM-DD HH:mm:ss ').toString()
);
setStore([...store, addtime]);
}
};
// to delete an item
const deleteItem = (index) => {
const updatedItems = items.filter((elem) => {
if (index == elem.id) {
elem.strike = true;
}
return elem;
});
setItems(updatedItems);
};
//to remove time
const deleteTime = () => {
setendTime(
moment().utcOffset('+05:30').format('YYYY-MM-DD HH:mm:ss ').toString()
);
};
// to remove all items
const removeAll = () => {
setItems([]);
setStore([]);
};
return (
<>
<div className="parent-div">
<div className="child-div">
<h3>TO DO LIST</h3>
<div className="addItems">
<input
type="text"
placeholder="what needs to be done?"
value={inputData}
onChange={(e) => setInputData(e.target.value)}
/>
<button
type="button"
onClick={addItem}
className="btn btn-secondary"
>
Create
</button>
</div>
<div id="wrapper">
<div id="left" className="showItems">
{items.map((elem) => {
console.log(elem);
return (
<div className="eachItem" key={elem.id}>
<h3
style={{
textDecoration: elem.strike ? 'line-through' : 'none',
}}
>
{elem.name}
</h3>
<button
type="button"
onClick={() => deleteItem(elem.id)}
className="btn btn-danger btn-sm"
>
Delete
</button>
</div>
);
})}
</div>
<div id="right">
{store.map((elim, ind) => {
return (
<div className="eachdate" key={ind}>
<h6>{'Task Created at:' + elim} </h6>
<button
type="button"
onClick={() => deleteTime()}
className="btn btn-danger btn-sm"
>
Delete
</button>
</div>
);
})}
</div>
</div>
<div className="showItems">
<button onClick={removeAll} className="btn btn-danger">
Delete All
</button>
</div>
</div>
</div>
</>
);
};
export default List;