Why is my delete function resulting in an undefined value error? - node.js

I'm building app using ReactJs and NodeJs. App is displaying data from database or all rooms. Room have to be abled to be created, edited and deleted.
Create and edit is working. The delete function is not.
Here is code:
Rooms.js
export default class RoomsAdmin extends React.Component {
constructor(props) {
super(props);
this.state = { rooms: [] };
this.delete = this.delete.bind(this);
}
componentDidMount() {
axios.get('http://localhost:3090/admin/')
.then(response => {
this.setState({ rooms: response.data });
})
.catch(function (error){
console.log(error);
})
}
componentDidUpdate() {
axios.get('http://localhost:3090/admin/')
.then(response => {
this.setState({ rooms: response.data });
})
.catch(function (error){
console.log(error);
})
}
delete() {
axios.get('http://localhost:3090/admin/delete/'+this.props.room._id)
.then(console.log('Deleted'))
.catch(err => console.log(err))
}
roomList() {
const Room = props => (
<div className="col-md-4">
<div className="card mb-4 shadow-sm">
<div className="card-body">
<h5 class="card-title">{props.room.title}</h5>
<p className="card-text">{props.room.description}</p>
<div className="d-flex justify-content-between align-items-center">
<div className="btn-group">
<Link className="btn btn-sm btn-outline-secondary"
to={ "/edit/"+props.room._id}>Edit</Link>
<button onClick={this.delete}>Delete</button>
</div>
</div>
</div>
</div>
</div>
)
return this.state.rooms.map(function (currentRoom, i) {
return <Room room={currentRoom} key={i} />
});
}
render() {
return (
<div>
<Header />
<div>
<div className="album py-5 bg-light">
<div className="container">
<div className="row">
{this.roomList()}
</div>
</div>
</div>
</div>
</div>
)
}
}
After clicking on delete I get this error:
Uncaught TypeError: Cannot read property '_id' of undefined
Any tip would be highly appreciated.

You are confusing a parent -> child relationship here. There is only one component, so you don't have props.room. Instead just pass the room id to be deleted in the callback function.
<button onClick={this.delete.bind(null, props.room._id}>Delete</button>
and your handler function
delete(id) {
I would rename your handler to be a little more explicit, also delete is a keyword in javascript so probably best to avoid that function name.
deleteRoom(id) {
axios.get(`http://localhost:3090/admin/delete/${id}`)
.then(console.log('Deleted'))
.catch(err => console.log(err))
Edit, I cleaned up the code a little, moved the child component outside of the parent so you dont need to reinitialize that component each time you render.
export default class RoomsAdmin extends React.Component {
constructor(props) {
super(props)
this.state = { rooms: [] }
this.deleteRoom = this.deleteRoom.bind(this)
}
componentDidMount() {
axios.get('http://localhost:3090/admin/')
.then(response => {
this.setState({ rooms: response.data })
})
.catch((error) => {
console.log(error)
})
}
componentDidUpdate() {
axios.get('http://localhost:3090/admin/')
.then(response => {
this.setState({ rooms: response.data })
})
.catch((error) => {
console.log(error)
})
}
deleteRoom(id) {
axios.get(`http://localhost:3090/admin/delete/${id}`)
.then(console.log('Deleted'))
.catch(err => console.log(err))
}
roomList() {
return this.state.rooms.map((currentRoom, i) => (
<Room
room={currentRoom}
onDelete={this.deleteRoom.bind(null, currentRoom._id)}
key={i}
/>
))
}
render() {
return (
<div>
<Header />
<div>
<div className="album py-5 bg-light">
<div className="container">
<div className="row">{this.roomList()}</div>
</div>
</div>
</div>
</div>
)
}
}
const Room = props => (
<div className="col-md-4">
<div className="card mb-4 shadow-sm">
<div className="card-body">
<h5 class="card-title">{props.room.title}</h5>
<p className="card-text">{props.room.description}</p>
<div className="d-flex justify-content-between align-items-center">
<div className="btn-group">
<Link
className="btn btn-sm btn-outline-secondary"
to={`/edit/${props.room._id}`}
>
Edit
</Link>
<button onClick={props.onDelete}>Delete</button>
</div>
</div>
</div>
</div>
</div>
)

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.

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,

Pass data from Vue front end to Node/Express backend

I want a user to be able to enter a city into a text input at the '/' route. Once that is submitted, id like to redirect to '/result' and show the info. The only way I can get '/result' to show what I want is if I hard code a city in the back end. How do I eliminate this?
GET request
app.get('/result', (req, res) => {
let city = 'Portland';
let url = `${process.env.BASEURL}${city}&units=imperial&APPID=${process.env.API_KEY}`;
axios.get(url)
.then(response => { res.json(response.data) })
.catch(error => {
return res.status(500).json({
success: false,
message: error.message
})
});
});
Service file that passes data from backend API
import axios from 'axios';
const url = "http://localhost:5000/result";
class WeatherService {
static getWeather() {
return new Promise((resolve, reject) => {
axios.get(url).then((res) => {
try {
resolve(res.data);
} catch (error) {
reject(error);
}
})
})
}
}
export default WeatherService;
Front end
<template>
<div>
<p class="error" v-if="error">{{ error }}</p>
<i class="fa fa-times close-icon none"></i>
<main>
<div class="location none">
<div class="city">{{ weather.name }}</div>
<div class="long-lat">{{ weather.coord.lon }}, {{ weather.coord.lat }}</div>
<div class="date">-, -:- pm</div>
</div>
<div class="main-content none">
<div class="tempIcon">
<div class="condition-icon"></div>
<div class="temp">{{ weather.main.temp }}<span>°</span></div>
</div>
<div class="weather">{{ weather.weather[0].main }}</div>
<div class="hi-low">{{ weather.main.temp_max }}° / {{ weather.main.temp_minl }}° <span></span>Feels like {{ weather.main.feels_like }}°</div>
</div>
</main>
<section class="details">
<div class="humidity none">
<i class="fa fa-tint"></i>
<h5>Humidity</h5>
<p class="humidity-value">{{ weather.main.humidity }}%</p>
</div>
<div class="pressure none">
<i class="fa fa-tachometer"></i>
<h5>Pressure</h5>
<p class="pressure-value">{{ weather.main.pressure }} hPa</p>
</div>
<div class="wind none">
<i class="fa fa-wind"></i>
<h5>Wind</h5>
<p class="wind-value">{{ weather.wind.speed }} mph</p>
</div>
</section>
</div>
</template>
<script>
import WeatherService from '../WeatherService';
export default {
name: 'Result',
data(){
return {
weather: [],
error: ''
}
},
async created() {
try {
this.weather = await WeatherService.getWeather();
} catch (error) {
this.error = error.message;
console.log(error);
}
}
}
</script>
You could pass some parameters like this
axios.get('/result', {
params: {
city: 'Portland'
}
})
This, of course, will need to be forwarded to your function to make it dynamic.
You can also use some headers or make a POST request rather than a GET. Not sure of the real differences between all those solutions tho.

Reactjs - setState doesn't re-render after update

Simply all i'm trying to do is re render the notes list when i add another note to the database. i tried several methods even redux dispatch method. but none worked and it kinda make sense because when i add a note i don't add anything so it can get the updated notes through /budget. maybe i have a big misunderstanding.
here's how i add a new note
export function saveOneNote() {
// saving a note
const _id = $('input[name="_id"]').val(),
firstItem = $('input[name="firstItem"]').val(),
firstPrice = $('input[name="firstPrice"]').val(),
secondItem = $('input[name="secondItem"]').val(),
secondPrice = $('input[name="secondPrice"]').val(),
thirdItem = $('input[name="thirdItem"]').val(),
thirdPrice = $('input[name="thirdPrice"]').val(),
tBudget = $('input[name="tBudget"]').val();
let currency = $("#currency").val();
console.log(currency);
$.ajax({
url: "/newNote",
type: "post",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({
currency,
_id,
firstItem,
firstPrice,
secondItem,
secondPrice,
thirdItem,
thirdPrice,
tBudget
}),
success: function(Data) {
console.log("note was saved!", Data);
},
error: function(err, status, xhr) {
console.log("err", err);
}
});
}
here's how i fetch notes
class ShowAll extends Component {
constructor(props){
super(props);
this.state = {
Data: [],
length:[],
searchbyid:[],
isLoggedIn:[]
}
}
componentDidMount(){
// fetch notes
Rquest.get('/budget').then((res)=>{
let DataString = Array.from(res.body);
this.setState((prevState,props)=>{
return {
Data: DataString,
length: res.body.length
}
})
}).catch((err)=> {
console.log(err);
})
// check if user is logged in
Request.get('/auth').then((user)=>{
if(user){
this.setState({
isLoggedIn: true
})
}
}).catch((err)=> {
this.setState({
isLoggedIn: false
})
});
}
render(){
const count = this.state.length;
const myNotes = this.state.Data;
const isLoggedIn = this.state.isLoggedIn;
const listItems = myNotes.map((dynamicData)=>{
return(
<Fragment key={dynamicData.id}>
<div className='jumbotron'>
<div className='row'>
<button className='btn btn-danger delete-note-btn' onClick={DeleteOneNote}>Delete</button>
<input className='col-12 title form-control' id='deleteById' value={dynamicData._id} readOnly/>
<div className="dropdown-divider"></div> {/*line divider*/}
<div className='col-6' >
<ul className='list-unstyled'>
<li className='items'>items</li>
<li >{dynamicData.firstItem}</li>
<li >{dynamicData.secondItem}</li>
<li >{dynamicData.thirdItem}</li>
{/* <li>Total Budget :</li> */}
</ul>
</div>
<div className='dynamicData col-6'>
<ul className ='list-unstyled'>
<li className='prices'>Prices</li>
<li>{dynamicData.firstPrice} {dynamicData.currency}</li>
<li>{dynamicData.secondPrice} {dynamicData.currency}</li>
<li>{dynamicData.thirdPrice} {dynamicData.currency}</li>
</ul>
</div>
</div>
<h3 className='col-12 totalprice'>{dynamicData.tBudget} {dynamicData.currency}</h3>
</div>
</Fragment>
)
})
return (
<Fragment>
{isLoggedIn ===true?(
<div className='myNotesList '>
number of notes : {count}
{listItems}
</div>
):(
<Fragment>
</Fragment>
)
}
</Fragment>
)
}
}
React components are re-rendering only on state or props change. In your code - you're not mutating state nor props of your component.
What you should do in your case probably is to re-fetch the items after save or add the new items to the state or pass through props.
Example:
class Notes extends React.Component {
state = { note: '', notes: [] }
changeNote = ({ target: { value } }) => {
this.setState({ note: value });
}
addNote = () => {
this.setState((state) => ({ notes: [...state.notes, state.note] }));
}
render() {
return (
<div>
<input type="text" onChange={this.changeNote} />
<button onClick={this.addNote}>Add</button>
<ul>
{this.state.notes.map(note =>
<li>{note}</li>
)}
</ul>
</div>
)
}
}
ReactDOM.render(
<Notes />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
<div id="app">
</div>

Resources