I'm trying to delete an order line from my MongoDB database using React, Redux and Express/Node but I'm getting the following error in my console:
VM118:1 DELETE http://localhost:3000/api/meals/:id/jp4PaZve3 404 (Not Found)
I'm not sure why it's pointing to port 3000, when my local server is running on 5000?
In my server file, I have the following delete endpoint created in Express
app.delete("/api/meals/:id", async (req, res) => {
const deletedMeal = await Meal.findByIdAndDelete(req.params.id);
res.send(deletedMeal);
});
In my redux actions I have the following (I'm not sure if this is correct):
export const deleteMeal = (id) => async (dispatch) => {
await fetch("/api/meals/:id/" + id, {
method: "DELETE",
});
dispatch({
type: DELETE_MEAL,
payload: id,
});
};
My UpdateMenu screen is as follows:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMeals, deleteMeal } from "../actions/mealActions";
class UpdateMenuScreen extends Component {
constructor(props) {
super(props);
this.state = {
meal: null,
};
}
componentDidMount() {
this.props.fetchMeals();
}
deleteMeal(id) {
this.props.deleteMeal(id);
}
render() {
return (
<div>
<h3>Current Menu</h3>
{!this.props.meals ? (
<div>Loading...</div>
) : (
<ul className="meals">
{this.props.meals.map((meal) => (
<li key={meal._id}>
<div className="meal">
<p>{meal.title}</p>
<button
className="button"
onClick={() => this.props.deleteMeal(meal._id)}
>
Delete
</button>
</div>
</li>
))}
</ul>
)}
<button>Add New Menu Item</button>
</div>
);
}
}
export default connect((state) => ({ meals: state.meals.items }), {
fetchMeals,
deleteMeal,
})(UpdateMenuScreen);
When I try and run my delete method in Postman however, it doesn't work. Can anyone see what I'm doing wrong?
In your deleteMeal action, you have to put the id in the URL dynamically with template strings,
await fetch("/api/meals/:id/" + id
1) it's equal to /api/meals/:id/id but according to your backend it should be /api/meals/:id
2) and you have to put the whole URL like http://localhost:5000/api/meals/${id} cause if you don't put the base, it will do a request on the port of your client so 3000
///////
So instead of :
export const deleteMeal = (id) => async (dispatch) => {
await fetch("/api/meals/:id/" + id, {
method: "DELETE",
});
dispatch({
type: DELETE_MEAL,
payload: id,
});
};
try this :
export const deleteMeal = (id) => async (dispatch) => {
await fetch(`http://localhost:5000/api/meals/${id}/`, {
method: "DELETE",
});
dispatch({
type: DELETE_MEAL,
payload: id,
});
};
Related
I want to build my next js project in which i am using
https://www.npmjs.com/package/#react-oauth/google
but when I build it i get the following :
this is layout.js and in _app.js I have all the components wrapped in GoogleOAuthProvider
import { GoogleLogin } from '#react-oauth/google';
import {FcGoogle} from "react-icons/Fc"
import { useGoogleLogin } from '#react-oauth/google';
export default function Layout({ children }) {
const client_id = ""
const responseGoogle = (response) => {
console.log(response);
}
CUTTED (NOT RELEVANT)
const login = useGoogleLogin({
onSuccess: codeResponse => {
const { code } = codeResponse;
console.log(codeResponse)
axios.post("http://localhost:8080/api/create-tokens", { code }).then(response => {
const { res, tokens } = response.data;
const refresh_token = tokens["refresh_token"];
const db = getFirestore(app)
updateDoc(doc(db, 'links', handle), {
refresh_token : refresh_token
})
updateDoc(doc(db, 'users', useruid), {
refresh_token : refresh_token
}).then(
CUTTED (NOT RELEVANT)
)
}).catch(err => {
console.log(err.message);
})
},
onError: errorResponse => console.log(errorResponse),
flow: "auth-code",
scope: "https://www.googleapis.com/auth/calendar"
});
return (
<>
CUTTED (NOT RELEVANT)
</>
)
}
Everything works perfect in dev mode but it does not want to build
I've faced this issue too. So I use 'GoogleLogin' instead of 'useGoogleLogin', then you can custom POST method on 'onSuccess' property.
import { GoogleLogin, GoogleOAuthenProvider} from '#react-oauth/google';
return(
<GoogleOAuthProvider clientId="YOUR CLIENT ID">
<GoogleLogin
onSuccess={handleLogin}
/>
</GoogleOAuthProvider>
The async function will be like...
const handleLogin = async = (credentialResponse) => {
var obj = jwt_decode(credentialResponse.credential);
var data = JSON.stringify(obj);
console.log(data);
const data = {your data to send to server};
const config = {
method: 'POST',
url: 'your backend server or endpoint',
headers: {},
data: data
}
await axios(config)
}
Spending whole day, this solve me out. Just want to share.
You have to wrap your application within GoogleOAuthProvider component. Please keep in mind that you will need your client ID for this.
import { GoogleOAuthProvider } from '#react-oauth/google';
<GoogleOAuthProvider clientId="<your_client_id>">
<SomeComponent />
...
<GoogleLoginButton onClick={handleGoogleLogin}/>
</GoogleOAuthProvider>;
So I'm having issues with a single component that displays a list pulled from a resource server. Then it uses Stompjs to establish a websocket and send messages. When I load the client, the Dev Console shows logs that it tries to call onConnected method() twice as my logs show two newUser messages sent from a single load of the component.
When I try to call the submitBid() method it throws a type error saying that
"TypeError: Cannot read properties of undefined (reading 'send')
at submitBid (AuctionList.js:76:1)"
Which I'm not sure why it would be undefined on that line when it's defined and running fine in the function on line 36 which runs before the method that fails. I've been stuck on this for several days so hopefully someone can tell me what I've got wrong in the code... Here is the component code....
import React from 'react'
import Stomp from 'stompjs';
import SockJS from 'sockjs-client';
import {useState, useEffect } from 'react';
function AuctionList({user, authCredentials, token}) {
const [auctionItems, setAuctionItems] = useState([]);
const [userData, setUserData] = useState({
email: user.email,
name: user.name,
message: ''
});
const [bid, setBid] = useState(0.00);
let stompClient;
let socket;
const connect = async () => {
socket = new SockJS('http://localhost:8080/ws')
stompClient = Stomp.over(socket)
stompClient.connect({}, onConnected, onError)
}
const onConnected = async () => {
stompClient.subscribe('/topic/bids', onMessageReceived)
stompClient.send("/app/socket.newUser",
{},
JSON.stringify({
sender: user.name,
type: 'NEW_USER',
time: Date.now()
})
)
}
const onError = async (err) => {
console.log(err);
}
const handleChange = async (e) =>{
setBid(e.target.value);
}
const submitBid = async (item) => {
let newMessage = {
type: "BID",
newBid: {
itemId: item.id,
email: user.email,
bidPrice: bid,
bidTime: new Date().getTime()
},
sender: userData.email,
time: new Date().getTime()
};
try {
stompClient.send("/socket.send", {}, JSON.stringify(newMessage));
} catch(err){
console.log(err); }
}
const onMessageReceived = async (payload)=>{
console.log("onMessageReceived")
console.log(payload)
}
const getAuctionList = async () => {
const url = "http://localhost:8080/auctionlist";
const init = {
method: "GET",
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${token}`, // notice the Bearer before your token
},
};
fetch(url, init)
.then(response => response.json())
.then(response => {setAuctionItems(response)})
};
useEffect( () => {
getAuctionList();
connect();
}, []);
return (
<ul>
{auctionItems.map( item => {
return( <div key={item.id} className = "auctionItemComponent">
<h3>{item.name}</h3>
<span>{item.desc}</span>
<span>Current Bid: ${item.itemStartingPrice}</span>
<span>Minimum Bid: {item.itemMinBid}</span>
<span>Time left</span>
<input type="number" id="bidInput_" name="bidInput" onChange={handleChange} ></input>
<button type='submit' onClick={submitBid}>Submit bid</button>
</div>)
})}
</ul>
)
}
export default AuctionList
Also I realize I have a bunch of async functions that don't have any awaits. I tried adding those in, but it was no change.
The issue here is not with stompjs but with the scoping. You have stompClient inside React Component but the one from submitBid is different. You can do it in different ways.
Put stompjs in global stage as in example here: https://playcode.io/972045
You can use useRef to have the client inside the React Component and have React do the tracking of any modifications.
I personally think something like a "connection" should stay away from inside a React Component. You should have the connection configs in a different file and import an instance to the JSX file.
i am currently working on an application in react and i am trying to add an extra feature to my code. I currently have the ability to add and delete money from my account and i have it linked to mongo. Working from here i want to take the dates and titles from my calendar portion of the app and put them in another collection in my database instead of storing them in state. So i have now set up the backend API for GET ADD DELETE in my database and it all work in Postmans and the database accepts the same values as the calendar prints in state memory. Theoretically i can just change the state value with the backend API but i am having trouble figuring out how to do this, i used axios in my other section the app so i am wondering how i would do it here, it doesn't have the same setup as my transactions file so its hard to transfer the same principles from what i've already done. Here is my calendar app with the GET ADD DELETE functions.
app2.js
import React, { useState, useEffect } from 'react';
import { CalHeader } from './CalHead';
import { Day } from './Day';
import { AddEvent } from './AddModal';
import { DelModal } from './DelModal';
import { useDate } from './hooks';
import "./style.css";
export const App2 = () => {
const [nav, setNav] = useState(0);
// Const to determine if somethings been clicked
const [clicked, setClicked] = useState(); // clicked, setClicked
// Const for Bills to be saved in local storage
const [bills, setBills] = useState( // bills, setBills
localStorage.getItem('bills') ?
JSON.parse(localStorage.getItem('bills')) :
[]
);
const eventForDate = date => bills.find(e => e.date === date );
useEffect(() => {
localStorage.setItem('bills', JSON.stringify (bills));
}, [bills] );
const { days, dateDisplay} = useDate( bills, nav );
return(
<>
<div>
<br></br>
</div>
<div id="container">
<CalHeader
dateDisplay = {dateDisplay}
onNext={() =>setNav(nav + 1)}
onBack={() =>setNav(nav - 1)}
/>
<div id="weekdays">
<div>Sunday</div>
<div>Monday</div>
<div>Tuesday</div>
<div> Wednesday</div>
<div>Thursday</div>
<div>Friday</div>
<div>Saturday</div>
</div>
<div id="calendar">
{days.map((d, index) => (
<Day
key={index}
day={d}
onClick={() => {
if (d.value !== 'padding') {
setClicked(d.date);
}
}}
/>
))}
</div>
</div>
{
clicked && !eventForDate(clicked) &&
<AddEvent
onClose={() => setClicked(null)}
onSave={title => {
setBills([ ...bills, { title, date: clicked }]);
setClicked(null);
}}
/>
}
{
clicked && eventForDate(clicked) &&
<DelModal
eventText={ eventForDate(clicked).title }
onClose={() => setClicked( null )}
onDelete={() => {
setBills(bills.filter( e => e.date !== clicked ));
setClicked(null);
}}
/>
}
</>
);
};
export default App2;
here is some of my backend i am unsure what you would want to see in the backend but here is the routes and controllers files
calendarRoutes.js
const express = require('express');
const router = express.Router();
const { getCalendars, addCalendars, deleteCalendars} = require('../controllers/calendars');
router
.route('/')
.get(getCalendars)
.post(addCalendars);
router
.route('/:id')
.delete(deleteCalendars);
module.exports = router;
calendars.js
const { response } = require('express');
const Calendar = require('../models/Calendar');
// #desc Get all dates
// #route GET /api/v1/dates
// #access Public
exports.getCalendars = async (req, res, next) => {
try{
const dates = await Calendar.find();
return res.status(200).json({
success: true,
count: dates.length,
data: dates
});
} catch (err) {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
exports.addCalendars = async (req, res, next) => {
try {
const { title, date} = req.body;
const dates = await Calendar.create(req.body);
return res.status(201).json({
succes:true,
data: dates
});
} catch (err) {
if(err.name === 'ValidationError') {
const messages = Object.values(err.errors).map(val => val.message);
return res.status(400).json({
success: false,
error: messages
});
} else {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
}
// #desc Delete transaction
// #route DELETE /api/v1/transaction/:id
// #access Public
exports.deleteCalendars = async (req, res, next) => {
try {
const transaction = await Calendar.findById(req.params.id);
if(!transaction) {
return res.status(404).json({
success: false,
error: 'No transaction found'
});
}
await transaction.remove();
return res.status(200).json({
success: true,
data: {}
});
} catch (err) {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
Any help would be appreciated, Let me know if you need any more files uploaed
I have a component with a checkbox that I'm keeping track of through a localhost db using postgres. When i check the checkboxes, a row is inserted into the table netowrkusers with the login user id in the id column, and the id of the user that was checked in the connections column. It removes the row when unchecked. It seems to work intermittently-but I keep getting a 'failed to fetch' error, and eventually the database doesn't keep proper track of the checked and unchecked boxes. Could someone that knows Knex see if there is a flaw in the code? Here is the Knex.js code I'm using.
app.post("/cb", (req, res) => {
const { loginuser, userid, ischecked } = req.body;
if (ischecked) {
console.log("flag is true");
db.transaction(trx => {
trx
.insert({
id: loginuser,
connections: userid
})
.into("networkusers")
.returning("id", "connections")
.then(() => {
console.log("committing");
trx.commit();
})
.catch(error => {
console.log("error", error);
trx.rollback();
});
}).catch(err => res.status(400).json(err));
} else {
console.log("flag is false");
db.transaction(trx => {
db("networkusers")
.where("id", "=", loginuser)
.andWhere("connections", "=", userid)
.del()
.returning("id", "connections")
.then(() => {
console.log("committing");
console.log(loginuser,userid)
trx.commit();
})
.catch(error => {
console.log("error", error);
trx.rollback();
});
}).catch(err => res.status(400).json(err));
}
});
And here is the components that have the checkbox logic:
import React, { useState } from "react";
const onUpdateCB = (ischecked, loginuser, userid, setisChecked,handleCheck) => {
console.log(ischecked, loginuser, userid);
fetch('http://localhost:3000/cb', {
method: 'post',
headers: {'Content-Type':'application/json'},
body:JSON.stringify({
loginuser,
userid,
ischecked: ischecked
})
}).then(setisChecked(ischecked));
return
};
const Card = props => {
const [isChecked, setisChecked] = useState(props.ischecked);
return (
<div
className="pointer bg-light-green dib br3 pa3 ma2 shadow-5"
onClick={() => props.handleClick(props.id)}
>
<div>
<h3>{props.name}</h3>
<p>{props.company}</p>
<p>{props.phone}</p>
<p>{props.email}</p>
<p>{props.city}</p>
</div>
<div>
My Network
<input
className="largeCheckbox"
type="checkbox"
checked={isChecked}
onChange={() =>
onUpdateCB(!isChecked, props.loginuser.id, props.id, setisChecked,props.handleCheck)
}
/>
</div>
</div>
);
};
export default Card;
NetworkArray component:
import React from "react";
import Card from "./Card";
const NetworkArray = ({
network,
networkusers,
handleChange,
handleClick,
loginuser
}) => {
console.log("in network array", networkusers);
const cardComponent = network.map((user, i) => {
const ischecked = networkusers.filter(n => {
var nw = n.id === loginuser.id && n.connections === network[i].id;
return nw;
});
console.log("is it checked", ischecked);
return (
<Card
key={network[i].id}
name={network[i].firstname + " " + network[i].lastname}
company={network[i].company}
phone={network[i].phone}
email={network[i].email}
city={network[i].city}
ischecked={ischecked.length}
handleChange={handleChange}
handleClick={handleClick}
id={network[i].id}
loginuser={loginuser}
/>
);
});
return <div>{cardComponent}</div>;
};
export default NetworkArray;
This doesn't look quite right:
db.transaction(trx => {
db("networkusers")
.where("id", "=", loginuser)
Normally you'd do:
db.transaction(trx => {
trx("networkusers")
.where("id", "=", loginuser)
or:
db.transaction(trx => {
db("networkusers")
.where("id", "=", loginuser)
.transacting(trx)
I suspect the issue is you're just not using the transaction object consistently. See overview.
You should also respond after your database operation succeeds (not just when it fails). So something like:
db
.transaction(trx => {
// ...query...
})
.then(() => res.json({ message: 'Success.' }))
.catch(err => res.status(400).json(err));
So, currently I'm working on internship React (MERN) app, which is a simple to-do list with ability to create, delete and edit todos. I will post some code from it, but you also can look at the full code on GitHub: https://github.com/Wonderio619/magisale-internship-todo
The next task is connecting my app to MongoDB. I have some "boilerplate" code - I alredy set up connection with MongoDB, also have Express router with routes like get all todos list, send todo to database, update todo with id, get todo with id:
const express = require("express");
const router = express.Router();
let Todo = require('../models/model')
// get all todo list with id
router.get('/', function (req, res) {
Todo.find()
.then((todos) => res.json(todos))
.catch((error) => res.send(error))
})
// send todo to database
router.post('/', function (req, res) {
let todo = new Todo();
todo.titleText = req.body.title;
todo.todoText = req.body.body;
todo.save(function (err) {
if (err)
res.send(err);
res.send('Todo successfully added!');
});
})
// get todo with id
router.get('/:todoId', function (req, res) {
Todo.findById(req.params.todoId)
.then(foundTodo => res.json(foundTodo))
.catch(error => res.send(error));
})
// updates todo with id
router.put('/:todoId', function (req, res) {
Todo.findOneAndUpdate({ _id: req.params.todoId }, req.body, { new: true })
.then((todo) => res.json(todo))
.catch((error) => res.send(error))
})
// deletes todo with id
router.delete('/:todoId', function (req, res) {
Todo.remove({ _id: req.params.todoId })
.then(() => res.json({ message: 'todo is deleted' }))
.catch((error) => res.send(error))
})
module.exports = router;
These routes used when corresponding methods from todo app are called:
import React, { Component } from 'react';
import './ToDo.css';
import Logo from './assets/logo.png';
import ToDoItem from './components/ToDoItem';
import AppBar from './components/AppBar';
import Popover from './components/Popover';
import { connect } from 'react-redux';
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
title: '',
todo: '',
};
};
componentDidMount = () => {
fetch("/api/todos")
.then(data => data.json())
.then(res => this.setState({ list: res.data }));
console.log(this.state.list)
};
createNewToDoItem = () => {
fetch("/api/todos", {
method: "post",
headers: new Headers({
"Content-Type": "application/json"
}),
body: JSON.stringify({
title: this.state.title,
body: this.state.todo
})
})
.catch(err => {
console.error(err);
});
if (this.state.title !== '' & this.state.todo !== '') {
this.props.createTodoItem(this.state.title, this.state.todo);
this.setState({ title: '', todo: '' });
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value,
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value,
});
};
editItem = (i, updTitle, updToDo) => {
const modifyURL = "/api/todos/" + i;
fetch(modifyURL, {
method: "put",
headers: new Headers({
"Content-Type": "application/json"
}),
body: JSON.stringify({
title: updTitle,
todo: updToDo
})
})
.then(resp => {
if (!resp.ok) {
if (resp.status >= 400 && resp.status < 500) {
return resp.json().then(data => {
let error = { errorMessage: data.message };
throw error;
});
} else {
let error = {
errorMessage: "Please try again later. Server is not online"
};
throw error;
}
}
return resp.json();
})
.then(newTodo => {
let arr = this.props.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState({ updateList: true });
});
};
deleteItem = indexToDelete => {
const deleteURL = "/api/todos/" + indexToDelete;
fetch(deleteURL, {
method: "delete"
})
.then(resp => {
if (!resp.ok) {
if (resp.status >= 400 && resp.status < 500) {
return resp.json().then(data => {
let error = { errorMessage: data.message };
throw error;
});
} else {
let error = {
errorMessage: "Please try again later. Server is not online"
};
throw error;
}
}
return resp.json();
})
.then(() => {
this.props.deleteTodoItem(indexToDelete);
});
};
randId() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10);
}
eachToDo = (item, i) => {
return <ToDoItem
key={this.randId()}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
};
render() {
const { list } = this.props;
return (
<div className="ToDo">
<img className="Logo" src={Logo} alt="React logo" />
<AppBar />
<div className="ToDo-Container">
<div className="ToDo-Content">
{list.map(this.eachToDo)}
</div>
<div>
<Popover
toDoValue={this.state.todo}
titleValue={this.state.title}
titleOnChange={this.handleTitleInput}
toDoOnChange={this.handleTodoInput}
addHandler={this.createNewToDoItem}
/>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
list: state.list
}
}
const mapDispatchToProps = dispatch => {
return {
deleteTodoItem: id => {
dispatch({ type: "DELETE_TODO", id: id });
},
createTodoItem: (title, todo) => {
dispatch({ type: "CREATE_TODO", title: title, todo: todo });
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ToDo);
Note that "list" array from state is not really used, bacause I have initial list state in Redux state here( it may be implemented bad, but it is anyway):
const initState = {
list: [
{
title: 'Cup cleaning',
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: 'Smoking rollton',
todo: 'Do some rollton and cigarettes'
},
{
title: 'Curious dream',
todo: 'Build a time machine'
}
],
};
const rootReducer = (state = initState, action) => {
switch (action.type) {
case "DELETE_TODO":
let newList = state.list.filter((todo, index) => action.id !== index)
return {
...state,
list: newList
}
case "CREATE_TODO":
const title = action.title;
const todo = action.todo;
let createdList = [
...state.list,
{
title,
todo
}
]
return {
...state,
list: createdList
}
default:
return state;
}
}
export default rootReducer;
So, now I need some help - if I understand everything right, my list state should now be stored inside MongoDB database. But currently it is in Redux, how should I switch from current state implementation to MongoDB properly ?
Also I understand that my MongoDB implementation is far from perfection, I'm just newbie to this, but I need to solve following problems:
1) I tried to get all todos from database in ComponentDidMount method and save it in array, but console.log always show that array is empty smth definitely wrong there.
2) Also connection with database is not really set up, because in general I can only add todos to database, but delete or edit functions does not work, because I'm little stuck about how to implement this index stuff, should I use ObjectId property from MongoDB or should I pass indexes from my main component to database, and how ?
Also any global recommendations regarding proper mongodb implementaion and suggestions or fixes to my code will be greatly appreciated :)
It's not res.data but res that you should inject in your state. res.data is undefined so it won't update the state.list.
componentDidMount = () => {
fetch("/api/todos")
.then(data => data.json())
.then(jsonData => {
console.log('jsonData --', jsonData)
console.log('jsonData.data is empty!', jsonData.data)
this.setState({ list: jsonData })
});
};
1- To be able to update, you're sending an id. You may create id's in your db if that's the way you want to find your todos.
Please note that _id is different from id.
_id mongodb's ObjectId, it is not of type integer but of type ObjectId.
id is just a regular field that you created that is called id.
NB: Your req.params.todoId is an integer. While ObjectId is of type ObjectId! So you won't be able to query one with the wrong type.
var todoSchema = new Schema({
id: Number,
titleText: String,
todoText: String
});
2- Get your todo and update it thanks to the id. If it does not exist, it will be created thanks to the upsert option. Don't forget to cast in order to match your schema. title: req.body.title won't work because you defined it as titleText in your schema.
// updates todo with id
router.put('/:todoId', function (req, res) {
const data = {
titleText: req.body.title,
todoText: req.body.todo
}
Todo.findOneAndUpdate(
{ id: req.params.todoId }, // the query
{ $set: data }, // things to update
{ upsert: true } // upsert option
).then((todo) => res.json(todo))
.catch((error) => res.send(error))
})