Strikethrough a list item in react todo list - node.js

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;

Related

cannot display fields in the web page reactjs

i was trying to make a restaurant review application using mern stack but in the frontend , i keep getting a problem in the react.
The pages load perfectly in the main page where the code as follows
import React, { useState, useEffect } from "react";
import RestaurantDataService from "../services/restaurant";
import { Link } from "react-router-dom";
const RestaurantsList = props => {
const [restaurants, setRestaurants] = useState([]);
const [searchName, setSearchName ] = useState("");
const [searchZip, setSearchZip ] = useState("");
const [searchCuisine, setSearchCuisine ] = useState("");
const [cuisines, setCuisines] = useState(["All Cuisines"]);
useEffect(() => {
retrieveRestaurants();
retrieveCuisines();
}, []);
const onChangeSearchName = e => {
const searchName = e.target.value;
setSearchName(searchName);
};
const onChangeSearchZip = e => {
const searchZip = e.target.value;
setSearchZip(searchZip);
};
const onChangeSearchCuisine = e => {
const searchCuisine = e.target.value;
setSearchCuisine(searchCuisine);
};
const retrieveRestaurants = () => {
RestaurantDataService.getAll()
.then(response => {
console.log(response.data);
setRestaurants(response.data.restaurants);
})
.catch(e => {
console.log(e);
});
};
const retrieveCuisines = () => {
RestaurantDataService.getCuisines()
.then(response => {
console.log(response.data);
setCuisines(["All Cuisines"].concat(response.data));
})
.catch(e => {
console.log(e);
});
};
const refreshList = () => {
retrieveRestaurants();
};
const find = (query, by) => {
RestaurantDataService.find(query, by)
.then(response => {
console.log(response.data);
setRestaurants(response.data.restaurants);
})
.catch(e => {
console.log(e);
});
};
const findByName = () => {
find(searchName, "name")
};
const findByZip = () => {
find(searchZip, "zipcode")
};
const findByCuisine = () => {
if (searchCuisine === "All Cuisines") {
refreshList();
} else {
find(searchCuisine, "cuisine")
}
};
return (
<div>
<div className="row pb-1">
<div className="input-group col-lg-4">
<input
type="text"
className="form-control"
placeholder="Search by name"
value={searchName}
onChange={onChangeSearchName}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={findByName}
>
Search
</button>
</div>
</div>
<div className="input-group col-lg-4">
<input
type="text"
className="form-control"
placeholder="Search by zip"
value={searchZip}
onChange={onChangeSearchZip}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={findByZip}
>
Search
</button>
</div>
</div>
<div className="input-group col-lg-4">
<select onChange={onChangeSearchCuisine}>
{cuisines.map(cuisine => {
return (
<option value={cuisine}> {cuisine.substr(0, 20)} </option>
)
})}
</select>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={findByCuisine}
>
Search
</button>
</div>
</div>
</div>
<div className="row">
{restaurants.map((restaurant) => {
const address = `${restaurant.address.building} ${restaurant.address.street}, ${restaurant.address.zipcode}`;
return (
<div className="col-lg-4 pb-1">
<div className="card">
<div className="card-body">
<h5 className="card-title">{restaurant.name}</h5>
<p className="card-text">
<strong>Cuisine: </strong>{restaurant.cuisine}<br/>
<strong>Address: </strong>{address}
</p>
<div className="row">
<Link to={"/restaurants/"+restaurant._id} className="btn btn-primary col-lg-5 mx-1 mb-1">
View Reviews
</Link>
<a target="_blank" href={"https://www.google.com/maps/place/" + address} className="btn btn-primary col-lg-5 mx-1 mb-1">View Map</a>
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
);
};
export default RestaurantsList;
but the problem arise when i click the view review button in the card shown in this page where the screen does not show anything and recieve anything kind of data. the code as follows:
import React, { useState, useEffect } from "react";
import RestaurantDataService from "../services/restaurant";
import { Link } from "react-router-dom";
const Restaurant = props => {
const initialRestaurantState = {
id: null,
name: "",
address: {},
cuisine: "",
reviews: []
};
const [restaurant, setRestaurant] = useState(initialRestaurantState);
const getRestaurant = id => {
RestaurantDataService.get(id)
.then(response => {
setRestaurant(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
getRestaurant(props.match.params.id);
}, [props.match.params.id]);
const deleteReview = (reviewId, index) => {
RestaurantDataService.deleteReview(reviewId, props.user.id)
.then(response => {
setRestaurant((prevState) => {
prevState.reviews.splice(index, 1)
return({
...prevState
})
})
})
.catch(e => {
console.log(e);
});
};
return (
<div>
{restaurant ? (
<div>
<h5>{restaurant.name}</h5>
<p>
<strong>Cuisine: </strong>{restaurant.cuisine}<br/>
<strong>Address: </strong>{restaurant.address.building} {restaurant.address.street}, {restaurant.address.zipcode}
</p>
<Link to={"/restaurants/" + props.match.params.id + "/review"} className="btn btn-primary">
Add Review
</Link>
<h4> Reviews </h4>
<div className="row">
{restaurant.reviews.length > 0 ? (
restaurant.reviews.map((review, index) => {
return (
<div className="col-lg-4 pb-1" key={index}>
<div className="card">
<div className="card-body">
<p className="card-text">
{review.text}<br/>
<strong>User: </strong>{review.name}<br/>
<strong>Date: </strong>{review.date}
</p>
{props.user && props.user.id === review.user_id &&
<div className="row">
<a onClick={() => deleteReview(review._id, index)} className="btn btn-primary col-lg-5 mx-1 mb-1">Delete</a>
<Link to={{
pathname: "/restaurants/" + props.match.params.id + "/review",
state: {
currentReview: review
}
}} className="btn btn-primary col-lg-5 mx-1 mb-1">Edit</Link>
</div>
}
</div>
</div>
</div>
);
})
) : (
<div className="col-sm-4">
<p>No reviews yet.</p>
</div>
)}
</div>
</div>
) : (
<div>
<br />
<p>No restaurant selected.</p>
</div>
)}
</div>
);
};
export default Restaurant;
please kindly answer the question if possible

Warning: Each child in a list should have a unique "key" prop and does not save in my json-server

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.

Node.js - React.js web app unable to render view with params in URL

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

Why the blog data is not passing to the database?

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,

How to pass my data in the backend in my axios reactjs

I have a problem and I am ensure this is my problem setusername(res.data[0].username)
So basically I watch MERN stack tutorial and I was at my this part 1:27:30. Basically It means to I want to pass my data username based on my list of users in creating exercises. Everything is fine but not only this setusername(res.data[0].username)
Here is the full code.. for Create_exercises()
NOTE I CONVERTED THE CLASS COMPONENT INTO REACT HOOKS SINCE IT KINDA LOOKS UGLY IN MY EYE
function Create_exercise() {
const [username,setusername] = useState()
const [description,setdescription] = useState()
const [duration,setduration] = useState()
const [date,setdate] = useState( new Date() )
const [users, setusers] = useState([])
const handleChangeUsername = e => {
setusername(e.target.value)
}
const handleChangeDesciption = e => {
setdescription(e.target.value)
}
const handleChangeDuration = e => {
setduration(e.target.value)
}
const handleChangeDate = date_new => {
setdate(date_new)
}
const handleSubmit = e => {
e.preventDefault()
const exercise = {
username: username,
description: description,
duration:duration,
date:date
}
console.log(exercise);
axios.post('http://localhost:4500/exercises/add',exercise)
.then(res => console.log(res.data))
.catch(err => console.log(err));
// window.location = '/';
}
useEffect(() => {
// setusers(['test user'])
// setusername('test user')
axios.get('http://localhost:4500/users/')
.then(res => {
if (res.data.length > 0) {
// console.log(users)
// console.log(res.data)
setusers(res.data.map((user => user.username)))
setusername(res.data[0].username)
// console.log(res.data[0].username)
}
})
},[])
return (
<div className='form-container'>
<form action="" onSubmit={e => handleSubmit(e)}>
<div className="title"><h1> Create New Exercise Log</h1></div>
<div className="form-group">
<label htmlFor="">Username: </label>
<select name="" id="">
{
users.map((user,idx) => {
return(
<option
key={user}
value={user}
>
{user}
</option>
)
})
}
</select>
</div>
<div className="form-group">
<label htmlFor="">Description: </label>
<input type="text"
required
className='form-control'
value={description}
onChange={e => handleChangeDesciption(e)}
/>
</div>
<div className="form-group">
<label htmlFor="">Duration (in minutes): </label>
<input type="text"
required
className='form-control'
value={duration}
onChange={e => handleChangeDuration(e)}
/>
</div>
<div className="form-group">
<label htmlFor="">Date: </label>
<div>
<DatePicker
selected= {date}
onChange= {e => handleChangeDate(e)}
>
</DatePicker>
</div>
</div>
<div className="form-group">
<input type="submit" value="Create Exercise Log"/>
</div>
</form>
</div>
)
}
export default Create_exercise
Let say I have 3 items in data that in my mongoDB.. Let say data = [{username:"Johnny",_id:"613fagadafa43d",description:"Egg"},{username:"Lamby",_id:"613dasnaafaF",description:"Betlog"},{username:"Bummby",_id:"613d66naafaF",description:"Betlog"}]
when I am trying to create a exercise that I will choose based on this three.. I always create the first item in data list. So it has something to with this setusername(res.data[0].username) since it is the first index.. I want to create a exercise wherein I can choose whose name should I create the data but I always get this thingy "Johnny" name in the data.. I wanna badly done this so I could understand how mern stack works..I'm so confused..help.
This is also same with Edit_exercise() based on the tutorial of MERN stack at part of MERN 1:45:06
Sorry I'm so bad at this explaining but this is just how I will imagined and describe the problem..I know its kinda simple but I don't know if I ask the right question.
function Edit_exercise() {
const { id } = useParams()
const [username,setusername] = useState()
const [description,setdescription] = useState()
const [duration,setduration] = useState()
const [date,setdate] = useState( new Date() )
const [users, setusers] = useState([])
const handleChangeUsername = e => {
setusername(e.target.value)
}
const handleChangeDesciption = e => {
setdescription(e.target.value)
}
const handleChangeDuration = e => {
setduration(e.target.value)
}
const handleChangeDate = date_new => {
setdate(date_new)
}
const handleSubmit = e => {
e.preventDefault()
const exercise = {
username: username,
description: description,
duration:duration,
date:date
}
console.log(exercise);
axios.put('http://localhost:4500/exercises/update/'+id,exercise)
.then(res => console.log(res.data))
.catch(err => console.log(err));
// window.location = '/';
}
useEffect(() => {
axios.get('http://localhost:4500/exercises/'+id)
.then(res => {
setusername(res.data.username)
setdescription(res.data.description)
setduration(res.data.duration)
setdate(new Date(res.data.date))
})
setusers(['test user'])
setusername('test user')
axios.get('http://localhost:4500/users')
.then(res => {
if (res.data.length > 0) {
setusers(res.data.map((user => user.username)))
setusername(res.data[0].username)
}
})
},[])
return (
<div className='form-container'>
<form action="" onSubmit={e => handleSubmit(e)}>
<div className="title"><h1> Edit Exercise Log</h1></div>
<div className="form-group">
<label htmlFor="">Username: </label>
<select name="" id="">
{
users.map((user,idx) => {
return(
<option
key={user}
value={user}
>
{user}
</option>
)
})
}
</select>
</div>
<div className="form-group">
<label htmlFor="">Description: </label>
<input type="text"
required
className='form-control'
value={description}
onChange={e => handleChangeDesciption(e)}
/>
</div>
<div className="form-group">
<label htmlFor="">Duration (in minutes): </label>
<input type="text"
required
className='form-control'
value={duration}
onChange={e => handleChangeDuration(e)}
/>
</div>
<div className="form-group">
<label htmlFor="">Date: </label>
<div>
<DatePicker
selected= {date}
onChange= {e => handleChangeDate(e)}
>
</DatePicker>
</div>
</div>
<div className="form-group">
<input type="submit" value="Edit Exercise Log"/>
</div>
</form>
</div>
)
}
export default Edit_exercise

Resources