Issue
When I make a request using postman to the server, I don’t get the "Cannot set headers after they are sent to the client. But when I integrate with react.js, i keep getting the error. I would for someone to help look for the error. Thanks. Thanks in advance."
Cart.js (client)
import React, { Fragment, useEffect, useState } from 'react'
import { Redirect } from 'react-router-dom';
import Header from '../../components/Header/Header'
import './Cart.css'
import axios from 'axios'
import Loader from '../../components/Loader/Loader';
import {toast} from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import CartProduct from '../../components/CartProduct/CartProduct';
import { Link } from 'react-router-dom';
import OrderDetails from '../../components/CartPricing/CartPricing';
import Empty from '../../components/Empty/Empty';
toast.configure();
function Cart({title, clientRootUrl, apiRootUrl, loggedInStatus, token, errorMessage, cartNum,decreaseCartNum, requireAuth}) {
document.title = `Cart - ${title}`;
/**
* To 2 d.p.
* Text: parseFloat("123.456").toFixed(2)
* Number: num.toFixed(2);
*/
const [isLoading, setIsLoading] = useState(true);
const [cartProducts, setCartProducts] = useState([]);
const [allowed, setAllowed] = useState(false);
const [subTotals, setSubTotals] = useState(0);
const [delivery, setDelivery] = useState(0); // use request to get details
const [total, setTotal] = useState(0);
const [all,setAll] = useState(false);
/** Pass the ffg data to App.js
* cart
* subtotal
* delivery
* total
*/
useEffect(() => {
// setIsLoading(true);
axios.post(`${apiRootUrl}miscellaneous/fee`,
{
subtotal: subTotals
},
{
headers: {
Authorization: `Bearer ${token}`
}
})
.then(({data})=>{
// setIsLoading(false);
setDelivery(data.cost);
})
.catch(err=>{
// setIsLoading(false);
console.log(err)
// toast.error(errorMessage, {
// position: toast.POSITION.BOTTOM_RIGHT
// })
})
}, [total])
useEffect(() => {
setTotal(subTotals + delivery);
}, [delivery])
useEffect(()=>{
axios.get(`${apiRootUrl}cart/`, {
headers: {
Authorization: `Bearer ${token}`
}
})
.then(({data})=>{
let cartItems = data;
// setCartProducts(data);
// console.log(cartItems)
if(cartItems.length < 1) {
setIsLoading(false);
} else {
cartItems.map(({id,productId,quantity})=>{
// cart: id, productId // product details
axios.get(`${apiRootUrl}product/${productId}`)
.then(({data})=>{
setIsLoading(false);
setAllowed(true); // show details
let product = data;
// no storing the previous one being asynchronous, so we have to use the previous in the argument
setCartProducts(prevCartProducts => [ ... prevCartProducts, {
cartId:id,
quantity:quantity,
id:product.id,
categoryId: product.categoryId,
name: product.name,
description: product.description,
image: product.image,
price: product.price
}]);
})
.catch(err=>{
setIsLoading(false);
console.log(err)
// toast.error(errorMessage, {
// position: toast.POSITION.BOTTOM_RIGHT
// })
})
})
}
})
.catch(err=>{
setIsLoading(false);
// toast.error(errorMessage, {
// position: toast.POSITION.BOTTOM_RIGHT
// })
console.log(err)
})
},[apiRootUrl])
function delCartItem(cartId) {
// delete from DB
setIsLoading(true);
axios.delete(`${apiRootUrl}cart/${cartId}`,{
headers: {
Authorization: `Bearer ${token}`
}
})
.then(({data})=>{
setIsLoading(false);
if(data.error === 0) {
toast.success(data.message, {
position: toast.POSITION.BOTTOM_RIGHT
})
setCartProducts([...cartProducts.filter(cartProduct=>cartProduct.cartId !== cartId)]);
// reduce cartNum by 1
decreaseCartNum();
} else {
toast.error(errorMessage, {
position: toast.POSITION.BOTTOM_RIGHT
})
}
if(cartProducts.length-1 < 1) {
setAllowed(false);
} else {
// as products are being deleted , RECALCULATE VALUES
// total prices will change authomatically
}
})
.catch(err=>{
setIsLoading(false);
// toast.error(errorMessage, {
// position: toast.POSITION.BOTTOM_RIGHT
// })
console.log(err)
})
}
function addSubTotals(subTotal) {
// setSubTotals(prevSubTotals=>prevSubTotals+subTotal);
}
useEffect(() => {
/** Get The Delivery Cost of The Products @ The Good Time */
setTotal(subTotals+delivery);
}, [subTotals])
useEffect(() => {
let sum = 0;
cartProducts.map((p)=>{
let subtotal = Number(p.price) * Number(p.quantity);
sum+=subtotal;
})
setSubTotals(sum);
}, [cartProducts])
function calculateNewSubTotalAndTotal(newQuantity, cartId) {
// update quantity and update of the specific cartId
const elementsIndex = cartProducts.findIndex(item=>item.cartId === cartId)
let newCartProducts = [...cartProducts];
newCartProducts[elementsIndex] = {...newCartProducts[elementsIndex], quantity:newQuantity}
setCartProducts(newCartProducts);
}
// function payWithPaystack(e) {
// e.preventDefault();
// let handler = PaystackPop.setup({
// key: 'sk_test_f12711e9277e1a27aba8e58f3394b9717098efaf', // Replace with your public key
// email: "[email protected]",
// amount: 1200 * 100,
// ref: ''+Math.floor((Math.random() * 1000000000) + 1), // generates a pseudo-unique reference. Please replace with a reference you generated. Or remove the line entirely so our API will generate one for you
// // label: "Optional string that replaces customer email"
// onClose: function(){
// alert('Window closed.');
// },
// callback: function(response){
// let message = 'Payment complete! Reference: ' + response.reference;
// alert(message);
// }
// });
// handler.openIframe();
// }
return (
<React.Fragment>
{requireAuth()}
{isLoading && <Loader />}
<Header title = {title} clientRootUrl = {clientRootUrl} loggedInStatus = {loggedInStatus} cartNum = {cartNum} token = {token} />
<br />
<br />
<div className = "small-container cart-page">
{
(cartProducts.length > 0) && (
<table>
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
{
cartProducts.map(({cartId,quantity,id,categoryId,name,description,image,price,out_of_stock})=><CartProduct key = {id} cartId = {cartId} quantity = {quantity} id = {id} categoryId = {categoryId} name = {name} description = {description} image = {image} price = {price} out_of_stock = {out_of_stock} apiRootUrl = {apiRootUrl} token = {token} errorMessage = {errorMessage} delCartItem = {delCartItem} addSubTotals = {addSubTotals} calculateNewSubTotalAndTotal = {calculateNewSubTotalAndTotal} />)
}
</table>
)
}
{
(cartProducts.length < 1) && (
<Fragment>
<Empty clientRootUrl = {clientRootUrl}>Ahh! Your cart seems empty.</Empty>
</Fragment>
)
}
{ (allowed) && (
<OrderDetails subTotals = {subTotals} delivery = {delivery} total = {total} >
<Link to = {`/checkout`} className = "btn">Proceed to Checkout</Link>
</OrderDetails>
)}
<br />
<br />
<br />
</div>
</React.Fragment>
)
}
export default Cart
app.js (server)
const express = require('express');
const app = express();
const morgan = require('morgan');
const bodyParser = require('body-parser');
const path = require('path');
const userRoutes = require('./api/routes/user');
const adminRoutes = require('./api/routes/admin');
const categoryRoutes = require('./api/routes/category');
const productRoutes = require('./api/routes/product');
const orderRoutes = require('./api/routes/order');
const marketRoutes = require('./api/routes/market');
const searchRoutes = require('./api/routes/search');
const cartRoutes = require('./api/routes/cart');
const miscellaneousRoutes = require('./api/routes/miscellaneous');
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
app.use((req,res,next) => {
res.header("Access-Control-Allow-Origin","*"); // change later
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
// res.header("Cache-Control", 'no-cache');
if(req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
res.status(200).json({});
}
next();
});
app.use('/user', userRoutes);
app.use('/admin', adminRoutes);
app.use('/category', categoryRoutes);
app.use('/product', productRoutes);
app.use('/order', orderRoutes);
app.use('/market', marketRoutes);
app.use('/search', searchRoutes);
app.use('/cart', cartRoutes);
app.use('/miscellaneous', miscellaneousRoutes);
// sample code to create static filepath
// app.use('/uploads', express.static('img'));
app.use('/uploads', express.static('uploads'));
app.use(express.static(path.join(__dirname,'public')));
app.use((req,res,next) => {
const error = new Error('Not found');
error.status = 404;
next(error);
});
app.use((error,req,res,next) => {
res.status(error.status || 500);
res.json({
error: {
message:error.message
}
});
});
module.exports = app;
cart.js (server(controller))
const pool = require('../../utils/pool');
exports.cart_get_all_for_user = (req,res,next) => {
const tokenUserId = req.userData.userId;
// const tokenEmail = req.userData.email;
// if(userId == tokenUserId || tokenEmail === process.env.adminEmail) {
pool.getConnection(function(err,conn){
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
conn.query(`select * from cartSchema where userId = ?`, [tokenUserId], function(err,result){
conn.release();
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
return res.status(200).json(result);
}
});
}
});
// res.end();
// } else {
// res.status(401).json({error:'No authorization!'});
// }
}
exports.add_to_cart = (req,res,next) => {
const { productId, quantity } = req.body;
const { userId } = req.userData;
pool.getConnection(function(err,conn){
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
pool.getConnection(function(err,conn){
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
conn.query(`select * from cartSchema where ( productId = ?) and ( userId = ? )`, [ productId,userId], function(err,cart_product){
// conn.release();
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
if(cart_product.length > 0) {
// update quantity in cart
conn.query(`update cartSchema set quantity = ? where ( productId = ?) and ( userId = ? )`, [quantity,productId,userId], function(err,result){
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
return res.status(200).json({error:455,message:'Product already exists in cart but the quantity has been updated'});
}
});
} else {
conn.query(`insert into cartSchema (userId, productId, quantity) values (?,?,?)`, [userId,productId, quantity], function(err,result){
conn.release();
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
return res.status(200).json({
error: 0,
message: 'Product has been added to cart',
id: result.insertId,
userId,
productId,
quantity
});
}
});
}
}
})
}
});
}
});
}
// update quantity
exports.update_cart_product_details = (req,res,next) => {
const {cartId} = req.params;
const {quantity} = req.body;
const {userId} = req.userData;
/**
* check if cartItem exists <i>
* check if the user is authorized to change <i>
* update cartItem
* give success message
*/
pool.getConnection((err, conn) => {
if (err) {
return res.status(500).json({ error: 'An error occured. Please try again!' });
} else {
conn.query(`select * from cartSchema where id = ? and userId = ?`, [cartId,userId], (err,cartItem) => {
if(err) {
return res.status(500).json({ error: 'An error occured. Please try again!' });
} else {
console.log(cartItem)
if(cartItem.length < 1) {
return res.status(500).json({ error: 'An error occured. Please try again!' }); // The CartItem you're trying to update does not exist
} else {
conn.query(`update cartSchema set quantity = ? where ( id = ? and userId = ? )`, [quantity,cartId,userId], (err,result)=>{
conn.release();
if(err) {
return res.status(500).json({ error: 'An error occured. Please try again!' });
} else {
return res.status(200).json({error:0,message:"Quantity updated successfully"});
}
})
}
}
});
}
})
}
exports.remove_from_cart = (req,res,next) => {
const { cartId } = req.params;
const {userId} = req.userData;
pool.getConnection(function(err,conn){
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
conn.query(`select * from cartSchema where id = ? and userId = ?`, [cartId,userId], (err,cartItem)=>{
if(err) {
return res.status(500).json({ error: 'An error occured. Please try again!' });
} else {
if(cartItem.length < 1) {
return res.status(500).json({ error: 'An error occured. Please try again!' }); // The CartItem you're trying to update does not exist
} else {
conn.query(`delete from cartSchema where id = ?`, [cartId], function(err,result){
conn.release();
if(err) {
return res.status(500).json({error:'An error occured. Please try again!'});
} else {
return res.status(200).json({error:0,message:'Product successfully removed from cart'});
}
});
}
}
})
}
});
}
miscellaneous.js
exports.city_cost = (req,res,next) => {
const {city} = req.params;
console.log(city)
return res.status(200).json({
fee:checkLocationFee(city)
})
}
exports.delivery_cost = (req,res,next) => {
const {userId} = req.userData;
/** Get the city of the user */
pool.getConnection((err,conn)=>{
if(err) {
logisticFees = logisticDefaultFee;
} else {
conn.query(`select * from userSchema where id = ?`, [userId], (err,user)=>{
if(err) {
logisticFees = logisticDefaultFee;
} else {
logisticFees = checkLocationFee(user[0].city);
}
});
}
});
console.log(logisticFees)
// const logisticFees = 1000; /** logistic fees varies based on LOCATION */
const { subtotal } = req.body;
/** FoodNet Logic */
let FoodNetFees;
if(subtotal < 1000) {
FoodNetFees = ((2.5/100)*subtotal) + 100; // 1.5 (down)
} else if(subtotal <= 5000) {
FoodNetFees = ((3/100)*subtotal) + 100;
} else if(subtotal > 5000 && subtotal <= 50000){
FoodNetFees = ((3.5/100)*subtotal) + 100;
} else { // above 50000
FoodNetFees = ((4/100)*subtotal) + 100;
}
/** ../end */
const paystackPaymentFee = ( subtotal + FoodNetFees + logisticFees ) * (1.5/100);
const paystackTransferFee = 10; // off transfer made to them
const totalCost = FoodNetFees + logisticFees + paystackPaymentFee + paystackTransferFee; // err to add subtotal
return res.status(200).json({
cost: totalCost
})
}
exports.verify_transaction = (req,res,next) => {
const {userId} = req.userData;
pool.getConnection((err,conn)=>{
if(err) {
logisticFees = logisticDefaultFee;
} else {
conn.query(`select * from userSchema where id = ?`, [userId], (err,user)=>{
if(err) {
logisticFees = logisticDefaultFee;
} else {
logisticFees = checkLocationFee(user[0].city);
}
});
}
});
const { reference } = req.params;
/** Verify Transaction (1) */
const promise = paystack.verifyTransaction({
reference
})
promise.then(function ({body}){
if(body.data.status === 'success') {
/** Create Transfer Recepient (2) */
const promise2 = paystack.createTransferRecipient({
type:"nuban",
name: logisticName,
account_number:logisticAccNumber,
bank_code:logisticBankCode,
currency:"NGN"
})
promise2.then(function({body}){
if(body.data.active === true) {
/** Make Transfer with Recipient(3) */
// store recipient_code
const recipient_code = body.data.recipient_code;
console.log(recipient_code)
/** JUST FOR DEVELOPMENT MODE ( REMOVE IN PRODUCTION MODE ) */
return res.status(200).json({error:0});
/** FOR PRODUCTION MODE BELOW */
// initiate transfer
const promise3 = paystack.initiateTransfer({
source:"balance",
reason: logisticReason,
amount: 900 * 100,
recipient: recipient_code,
reference: Math.floor((Math.random() * 1000000000) + 1)
})
promise3.then(function({body}){
if(body.data.status === "success") {
// will only work with real transactions
res.status(200).json({error:0})
} else {
/** Handle Error */
return res.status(500).json({error:'An error occured. Please try again!'})
}
}).catch(function(err){
/** Handle Error */
return res.status(500).json({error:'An error occured. Please try again!'})
});
/** FOR PRODUCTION MODE ABOVE */
} else {
/** Handle Error */
return res.status(500).json({error:'An error occured. Please try again!'})
}
})
} else {
/** Handle Error */
// console.log(2)
return res.status(500).json({error:'An error occured. Please try again!'})
}
}).catch(function(err){
/** Handle Error */
return res.status(500).json({error:'An error occured. Please try again!'})
})
// put return in all response
}
Solution
The problem got solved. I was making multiple requests to the server being asynchronous code. So i just nested my requests to the stay in the safe space and all worked perfectly.