API getting called multiple times - node.js

When testing the API I have noticed that it is getting called around 6 times, there is no for/foreach loop that would make it run several times in the code so am unsure as to why this may be happening.
The API runs every time a user goes onto the Landing Screen.
Any advice would be appreciated
exports.getFilteredOpportunities = async (req, res) => {
let mongoQuery = generateMongoQuery(req);
try {
const opps = await Opportunity.find(mongoQuery)
.select(removeItems)
.sort({
createdAt: -1
});
res.status(200).json({
opportunities: opps,
});
} catch (error) {
res.status(500).json({
status: "error",
message: error,
});
}
};
GenerateMongoQuery function
const generateMongoQuery = (req) => {
let query = {};
// search param used searching through Title field, ie. search=food
if (req.query.search && req.query.search.length > 0) {
query.$or = [{}, {}];
query.$or[0].Title = query.$or[1].Description = new RegExp(
`${req.query.search}`,
"i"
);
}
// location param, i.e location=Glasgow,Manchester
if (req.query.location && req.query.location.length > 0) {
query.location = {
$in: req.query.location.split(",")
};
}
// timeslot param, i.e timeslot=Evening,Morning
if (req.query.timeslot && req.query.timeslot.length > 0) {
query.timeslot = {
$in: req.query.timeslot.split(",")
};
}
// category param, returning corresponding id i.e category=
if (req.query.category && req.query.category.length > 0) {
query.category = {
$in: req.query.category.split(",")
};
}
// Dont load expired opportunities
query.eventDate = {
$gte: new Date().toDateString()
};
// Only return non-cancelled opportunities
query.isCancelled = false;
return query;
};
The landing Screen
import { useState, useEffect } from "react";
import Opportunities from "../components/molecules/Opportunities";
import Filters from "../components/organisms/Filters";
import { IOpportunity } from "../Models/IOpportunity";
import OpportunitiesClient from "../Api/opportunitiesClient";
import Utils from "../Utils/Utils";
import FiltersClient from "../Api/filtersClient";
import { IFilters } from "../Models/IFilters";
import { ISelectedFilters } from "../Models/ISelectedFilters";
import Header from "../components/atoms/Header";
import Footer from "../components/atoms/Footer";
export default function LandingScreen(props) {
const [opportunities, setOpportunities] = useState<IOpportunity[]>([]);
const [filters, setFilters] = useState<IFilters[]>([]);
const [checkedFilters, setCheckedFilters] = useState<
ISelectedFilters | undefined
>({
Location: [],
Category: [],
Timeslot: [],
});
const [isLoading, setLoading] = useState<boolean>(true);
const [allResultsLoaded, setAllResultsLoaded] = useState<boolean>(false);
let pageToGet = 1;
const [scrollPosition, setScrollPosition] = useState(0);
const [totalOpps, setTotalOpps] = useState(0);
useEffect(() => {
getOpportunities();
getFilters();
}, []);
useEffect(() => {
setTotalOpps(opportunities.length);
}, [opportunities]);
const handleScroll = () => {
setScrollPosition(window.pageYOffset);
let scrollHeight = 0;
if ((props.scrollHeight === null) !== undefined) {
scrollHeight = +props.scrollHeight;
} else {
scrollHeight = document.scrollingElement.scrollHeight;
}
if (
window.innerHeight + document.documentElement.scrollTop >=
scrollHeight
) {
if (allResultsLoaded === false) {
setLoading(true);
}
setTimeout(() => {
pageToGet += 1;
getOpportunities();
}, 600);
}
window.removeEventListener("scroll", handleScroll);
};
window.addEventListener("scroll", handleScroll, { passive: true });
const setItemChecked = (filtername: string, filterType: string) => {
// reset page and results
pageToGet = 1;
setAllResultsLoaded(false);
setCheckedFilters(
Utils.setItemChecked(filtername, filterType, checkedFilters)
);
};
const resetFilters = () => {
// reset page and results
pageToGet = 1;
setAllResultsLoaded(false);
checkedFilters.Location = [];
checkedFilters.Category = [];
checkedFilters.Timeslot = [];
getOpportunities();
};
const getFilters = () => {
FiltersClient.getAll().then((filters) => {
setFilters(filters);
});
};
const getOpportunities = () => {
console.log(opportunities);
OpportunitiesClient.getWithParams(
Utils.getAxiosParams(checkedFilters)
).then((res) => {
definePage(res);
if (opportunities.length === res["opportunities"].length)
setAllResultsLoaded(true);
setLoading(false);
});
};
const definePage = (res) => {
if (pageToGet === 1) {
setOpportunities(res["opportunities"].slice(0, 15));
} else {
setOpportunities(
opportunities.concat(
res["opportunities"].slice(
opportunities.length,
opportunities.length + 15
)
)
);
}
};
return (
<div>
<Header page="landing" includePostButton={true} />
<div
data-testid="test-div1"
className="grid md:grid-cols-[150px_auto] sm:grid-cols-1 md:grid-flow-col gap-4 mx-4 mb-5 my-10"
>
<div></div>
<div className="card-container-title">
{totalOpps} Results{" "}
{checkedFilters.Location.length > 0 ? "(Filtered)" : ""}
</div>
</div>
<div
data-testid="test-div3"
className="grid md:grid-cols-[150px_auto] sm:grid-cols-1 md:grid-flow-col gap-4 mx-4"
>
<div>
<Filters
filters={filters}
getChecked={setItemChecked}
urlCall={getOpportunities}
resetFilters={resetFilters}
checkedFilters={checkedFilters}
/>
</div>
<div
data-testid="test-div4"
className={isLoading ? "opacity-50" : ""}
>
<div className="col-span-2">
<Opportunities
items={opportunities}
isLoading={isLoading}
allResultsLoaded={allResultsLoaded}
/>
</div>
</div>
</div>
<Footer />
</div>
);
}

Related

Have a Handler Change Value of a Variable in Mongodb

I am working on a product where I have pre orders and normal orders. For my pre order items, I have the variable pre_orderQty, and for my normal items, I have the variable qty. In my OrderScreen.js, I have a handler that deals with if an order has been shipped (shipHandler). The issue that I am having is that I am trying to get this handler to also update any items in my order that have a qty value of 0 to the value of the pre_orderQty when clicked.
I am using node in my backend and react in my frontend. The orders are stored in mongodb. I'm not exactly sure how to do this, and I would really appreciate any help or advice on how to accomplish this.
So far, I have attempted to do this by:
In my backend orderRouter.js
orderRouter.put(
'/:id/updated',
isAuth,
isAdmin,
expressAsyncHandler(async (req, res) => {
const order = await Order.findById(req.params.id);
if (order && order.orderItems.qty === 0) {
order.orderItems.qty = order.orderItems.pre_orderQty;
order.orderItems.pre_orderQty = 0;
const updatedOrder = await order.save();
res.send({ message: 'Order Updated', order: updatedOrder });
} else {
res.status(404).send({ message: 'Order Not Found' });
}
}),
);
Frontend:
OrderActions.js
export const updateOrder = (orderId) => async (dispatch, getState) => {
dispatch({ type: ORDER_UPDATE_REQUEST, payload: orderId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = Axios.put(
`/api/orders/${orderId}/updated`,
{},
{
headers: { Authorization: `Bearer ${userInfo.token}` },
},
);
dispatch({ type: ORDER_UPDATE_SUCCESS, payload: data });
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message;
dispatch({ type: ORDER_UPDATE_FAIL, payload: message });
}
};
OrderScreen
import Axios from 'axios';
import { PayPalButton } from 'react-paypal-button-v2';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import {shipOrder, deliverOrder, detailsOrder, payOrder, updateOrder } from '../actions/orderActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {
ORDER_DELIVER_RESET,
ORDER_SHIPPED_RESET,
ORDER_RETURN_RESET,
ORDER_PAY_RESET,
} from '../constants/orderConstants';
export default function OrderScreen(props) {
const orderId = props.match.params.id;
const [sdkReady, setSdkReady] = useState(false);
const orderDetails = useSelector((state) => state.orderDetails);
const { order, loading, error } = orderDetails;
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const orderShip = useSelector((state) => state.orderShip);
const {
loading: loadingShip,
error: errorShip,
success: successShip,
} = orderShip;
const orderUpdate = useSelector((state) => state.orderUpdate);
const {
loading: loadingUpdate,
error: errorUpdate,
success: successUpdate,
} = orderUpdate;
const dispatch = useDispatch();
useEffect(() => {
if (
!order ||
successShip ||
successUpdate ||
(order && order._id !== orderId)
) {
dispatch({ type: ORDER_SHIPPED_RESET });
dispatch({ type: ORDER_UPDATE_RESET });
dispatch(detailsOrder(orderId));
} else {
if (!order.isPaid) {
if (!window.paypal) {
addPayPalScript();
} else {
setSdkReady(true);
}
}
}
}, [dispatch, order, orderId, sdkReady, successShip, successUpdate, order]);
const shipHandler = () => {
dispatch(shipOrder(order._id));
dispatch(updateOrder(order._id));
};
return loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<h1>Order {order._id}</h1>
<div className="row top">
<div className="col-1">
<div className="card card-body">
<ul>
{userInfo.isAdmin && (
<li>
{loadingShip && <LoadingBox></LoadingBox>}
{errorShip && (
<MessageBox variant="danger">{errorShip}</MessageBox>
)}
<button
type="button"
className="primary block"
onClick={shipHandler}
>
Order Shipped
</button>
</li>
)}
</ul>
</div>
</div>
</div>
</div>
);
}

How to fix/trouble shoot the missing response from nodejs service?

I am trying to trouble shoot/fix my nodejs upload image api:
My service is being stuck at somewhere.
My service is too simple, just uploading the image by resizing through sharp api in a directory and return the full path of that file.
When I select some image at first time then everything works fine and image upload successfully with a response
but When I try to crop that image after clicking on the image and try to save it (at second time) then nodejs service return following response.
I don't why it is being happened, I tried to debug the service code and It didn't stop at anywhere. Flow has been passed/executed successfully, I didn't catch any exception/error in code.
What can be the issue in it because it still displaying
> Blockquote failed to load response data. no resource with given identifier found
Problem area is in the code of onSaveImage when a server side call is being served/requested. I am using the plugin for image cropping is react-easy-crop. Browser is getting refresh/reload at this line of code
const jsonRes = await responseMain.json();
I am sharing my nodejs service code as well as reactjs code. please look into it.
Thank you.
-----------------package.json of nodejs
{
"name": "",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-validator": "^6.12.0",
"handlebars": "^4.7.7",
"jsonwebtoken": "^8.5.1",
"mysql": "^2.18.1",
"nodemailer": "^6.6.1",
"nodemon": "^2.0.12",
"sharp": "^0.29.3"
},
"devDependencies": {},
"scripts": {
"start": "nodemon --inspect index.js",
"debug": "node --nolazy --inspect-brk=9229 index.js"
},
"license": "ISC"
}
------ index.js---------------------------NodeJs
const express = require("express");
const app = express();
const cors = require("cors");
var fs = require("fs");
const fsp = fs.promises;
var path = require("path");
const sharp = require("sharp");
var $pathContentBuilder = path.join(__dirname, "../", "/public/roughdata/uploads/");
var $urlpathContentBuilder = "/roughdata/uploads/"; // URL path
app.use(express.json({ limit: "20mb" }));
app.use(cors());
app.use(
express.urlencoded({
extended: true,
})
);
function processImage(image, metadata, filename, isResizeAllow) {
return new Promise((resolve, reject) => {
if (isResizeAllow && isResizeAllow === true) {
// 0.8 MB
return image
.resize({
width: Math.round(metadata.width / 2),
height: Math.round(metadata.height / 2),
fit: "cover",
})
.toBuffer((err, buf) => {
if (err) {
console.log("Error occured ", err);
return reject(err);
} else {
return resolve(buf.toString("base64"));
}
});
} else {
return image.toBuffer((err, buf) => {
if (err) {
console.log("Error occured ", err);
return reject(err);
} else {
return resolve(buf.toString("base64"));
}
});
}
});
}
app.post("/uploaddetail", async (req, res, next) => {
const base64Data = req.body.image;
const filename = req.body.filename;
let imageResizeResult = "";
try {
var inputbuf = Buffer.from(base64Data, "base64"); // Ta-da
const image = await sharp(inputbuf);
let metadata = await image.metadata();
let convertedbase64Data = "";
convertedbase64Data = await processImage(image, metadata, filename, false);
await fsp.writeFile($pathContentBuilder + filename, convertedbase64Data, "base64");
let resultResponse = JSON.stringify({
success: true,
fullurl: `${$urlpathContentBuilder}${filename}`,
url: `${filename}`,
imagename: `${filename}`,
serverpath: `${$urlpathContentBuilder}`,
});
//res.type("json");
res.status(200).json(resultResponse);
//res.end();
//next();
} catch (e) {
console.log(e);
const error = new HttpError(e, 404);
return next(error);
}
});
and following is my reactjs code.
import React, { useState, useEffect, useCallback } from "react";
import { withRouter, useParams, useHistory } from "react-router-dom";
import { Card, Button } from "react-bootstrap";
import Page from "./Page";
import TitleBar from "./TitleBar";
import SingleGalleryRow from "./SingleGalleryRow";
import CenteredPopup from "../common/CenteredPopup";
import { MdDelete, MdOutlineCrop, MdOutlineCircle } from "react-icons/md";
import Cropper from "react-easy-crop";
import getCroppedImg from "../shared/util/cropImage";
import b64toBlob from "../shared/util/blobToImage";
function EditImage({ breadcrumb, pageName, setLoader, showToast }) {
const [list, setList] = useState([]);
const history = useHistory();
const [umodalShow, setUModalShow] = useState(false);
const [row, setRow] = useState({
ImageName: "",
ImageServerPath: "",
ImageFullUrl: "",
ImageUrl: "",
SiteId: 1,
CreatedBy: 1,
CreatedOn: "",
Id: 0,
Name: "",
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
});
const [isSubmitClicked, setIsSubmitClicked] = useState(false);
const [IsDisabled, setIsDisabled] = useState(false);
const [nameError, setNameError] = useState("");
const [validated, setValidated] = useState(0);
const [ImageName, setImageName] = useState("");
const [base64SelectedImage, setBase64SelectedImage] = useState("");
const { id } = useParams();
let content = "";
const [detail, setDetail] = useState({
Content: "",
CreatedBy: 1,
CreatedOn: "",
Id: 0,
IsDeleted: 0,
IsVisible: 1,
ModifiedBy: 1,
ModifiedOn: "",
Name: "",
SiteId: 1,
});
useEffect(() => {
let abortController = new AbortController();
let mounted = true;
const fetchData = async () => {
try {
setLoader(true);
let apiUrl = process.env.REACT_APP_IMAGEGALLERYAPI + "/getbyId/" + id;
const response = await fetch(apiUrl, {
signal: abortController.signal,
});
const json = await response.json();
const resultPages = json ? json.list : "";
if (resultPages && resultPages.length > 0) {
//debugger;
let firstRow = resultPages[0];
let isEnabled = false;
if (firstRow.IsVisible === 1 || firstRow.IsVisible === true) {
isEnabled = true;
}
let newDetailPage = {
Id: firstRow.Id,
Name: firstRow.Name,
Content: firstRow.Content
};
if (mounted) {
setDetail(newDetailPage);
}
let itemRows = [];
for (let item of resultPages) {
let row = {
Id: item.DetailId,
ImageSliderId: item.ImageSliderId,
ImageName: item.ImageName,
ImageFullUrl: item.ImageFullUrl,
};
if (item.ImageFullUrl) {
itemRows.push(row);
}
}
if (itemRows && itemRows.length > 0) {
if (mounted) {
setList(itemRows);
}
}
}
setLoader(false);
//abortController = null;
} catch (err) {
setLoader(false);
if (err.name === "AbortError") {
// Handling error thrown by aborting request
}
}
};
fetchData();
// Side-effect logic...
return () => {
// Side-effect cleanup
mounted = false;
abortController.abort();
console.log("unmounting...");
};
}, []);
const [imageCropWidth, setImageCropWidth] = useState(100);
const [imageCropHeight, setImageCropHeight] = useState(100);
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [aspect, setAspect] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
const [croppedImage, setCroppedImage] = useState(null);
const [cropSize, setCropSize] = useState({ width: 100, height: 100 });
const [Image, setImage] = useState("");
const [rotation, setRotation] = useState(0);
const [cropShape, setCropShape] = useState("rect");
const onSaveImage = useCallback(
async (e) => {
e.preventDefault();
if (validate() < 0) {
return -1;
}
try {
let filename = "";
let base64Image = "";
let blobUrl = "";
debugger;
const cropImage = await getCroppedImg(Image, croppedAreaPixels, rotation);
setCroppedImage(cropImage);
setImage(cropImage);
base64Image = cropImage ? cropImage.replace(/^data:image\/(png|jpeg);base64,/, "") : "";
const contentType = "image/jpeg";
if (cropImage) {
debugger;
setRow((prevState) => {
return {
...prevState,
["ImageFullUrl"]: cropImage,
["ImageName"]: ImageName ? ImageName : "",
};
});
filename = ImageName ? ImageName : "";
const settings = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
filename: filename,
image: base64Image,
}),
};
setLoader(true);
let submitUrl = process.env.REACT_APP_SERVER_DOMAIN + `/uploaddetail`;
const responseMain = await fetch(submitUrl, settings);
const jsonRes = await responseMain.json();
if (jsonRes) {
let convertedJson = jsonRes;
if (convertedJson) {
setLoader(false);
const uploadedImageUrl = convertedJson.fullurl; // get saved image url
const uploadedImageName = convertedJson.imagename;
const uploadedServerPath = convertedJson.serverpath;
const uploadurl = convertedJson.url;
setImageName(convertedJson.imagename);
setRow((prevState) => {
return {
...prevState,
["ImageName"]: uploadedImageName,
["ImageServerPath"]: uploadedServerPath,
["ImageFullUrl"]: uploadedImageUrl,
["ImageUrl"]: uploadurl,
};
});
if (uploadedImageUrl) {
setIsDisabled(false);
}
setUModalShow(false);
setDetail((prevState) => {
return {
...prevState,
["Content"]: content,
};
});
}
}
}
} catch (err) {
console.log(err);
showToast("Error!", "Image couldn't be saved");
} finally {
}
},
[croppedAreaPixels, rotation]
);
const showImageForCrop = useCallback(
async (e) => {
debugger;
let localSrc = e.ImageFullUrl; //e.target.src;
setImage(localSrc);
setUModalShow(true);
setImageName(e.ImageName);
setCropSize({ width: imageCropWidth ? parseInt(imageCropWidth) : 0, height: imageCropHeight ? parseInt(imageCropHeight) : 0 });
if (list && list.length > 0) {
const selectedIndex = list.findIndex((item) => item.Id === e.Id);
if (selectedIndex > -1) {
setRow(list[selectedIndex]);
}
}
},
[row]
);
const singlefileSelectedHandler = async (e) => {
//debugger;
e.preventDefault();
setIsDisabled(true);
content = "";
if (!e.target.files || e.target.files.length <= 0) {
return -1;
}
if (detail && detail.Content) {
content = detail.Content;
}
//let imageList = "";
const selectedImage = e.target.files[0];
let imageSizeInMBs = selectedImage.size ? selectedImage.size / 1024 / 1024 : 0;
if (selectedImage && imageSizeInMBs > 8) {
setIsDisabled(false);
setRow((prevState) => {
return {
...prevState,
["ImageName"]: "",
["ImageServerPath"]: "",
["ImageFullUrl"]: "",
["ImageUrl"]: "",
};
});
showToast("Information!", "Image size can't be greater than 8MB.");
return -1;
}
const filename =
selectedImage && selectedImage.name
? new Date().valueOf() +
"_" +
selectedImage.name.replace(/\\/g, "").replace(/ /g, "").replace(/'/g, "").replace(/"/g, "").replace(/`/g, "")
: "";
setUModalShow(false);
setCropSize({ width: imageCropWidth ? parseInt(imageCropWidth) : 0, height: imageCropHeight ? parseInt(imageCropHeight) : 0 });
setImageName(filename);
setUModalShow(true);
let convertedImageFile = await convertImage(selectedImage);
setBase64SelectedImage(convertedImageFile);
};
async function convertImage(file) {
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
e.preventDefault();
let base64 = e.target.result;
base64 = base64.replace(/^data:image\/(png|jpeg);base64,/, "");
setImage(reader.result);
resolve(base64);
//resolve(reader.result);
};
reader.onerror = function (error) {
console.log("Error: ", error);
reject(error);
};
});
}
const onAddSide = (e) => {
//debugger;
let newImageList = [...list];
if (row.Id && row.Id > 0) {
if (newImageList && newImageList.length > 0) {
const selectedIndex = newImageList.findIndex((item) => item.Id === row.Id);
if (selectedIndex > -1) {
newImageList[selectedIndex] = row;
}
}
} else {
row.Id = Math.max.apply(
Math,
newImageList.map(function (o) {
return o.Id + 1;
})
);
if (!row.Id || (row.Id && row.Id === 0) || row.Id === -Infinity) {
row.Id = 1;
}
newImageList.push(row);
}
setList(newImageList);
};
const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
//debugger;
setCroppedAreaPixels(croppedAreaPixels);
}, []);
const onClose = useCallback(() => {
setCroppedImage(null);
}, []);
const onCropperZoomChange = (e) => {
if (e && e.currentTarget && e.currentTarget.value) {
let lZoomValue = e.currentTarget.value;
if (lZoomValue && lZoomValue < 0.4) {
//setZoom(0.28);
setZoom(0.4);
} else if (lZoomValue && lZoomValue > 1) {
setZoom(1);
} else {
setZoom(lZoomValue);
}
}
};
return (
<Page breadcrumb={breadcrumb} pageName={pageName} fluid>
<form noValidate validated={validated} onSubmit={HandleSaveMenu} className="user-form">
<Card>
<Card.Header>
<TitleBar title="Edit Gallery" />
</Card.Header>
<Card.Body>
<div className="form-row">
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Width
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropWidth}
onChange={(e) => setImageCropWidth(e.target.value)}
/>
</div>
<div className="form-group col-md-2">
<label className="form-label" htmlFor="ImageCropWidth">
Height
</label>
<input
name="ImageCropWidth"
id="ImageCropWidth"
className={`form-control`}
value={imageCropHeight}
onChange={(e) => setImageCropHeight(e.target.value)}
/>
</div>
</div>
<SingleGalleryRow
data={row}
setRow={setRow}
onUpload={singlefileSelectedHandler}
onAddSide={onAddSide}
IsDisabled={IsDisabled}
/>
<ul className="slide-list">
{list &&
list.map(function (item, i) {
//debugger;
return (
<li key={i}>
<div className="slide-row">
<div className="img-holder">
<img
className="thumbnail"
src={item.ImageFullUrl}
alt={item.ImageName}
onClick={() => showImageForCrop(item)}
/>
</div>
</div>
</li>
);
})}
</ul>
</Card.Body>
<Card.Footer>
<div className="btn-holder text-right">
<Button type="submit" variant="primary" disabled={isSubmitClicked}>
Save
</Button>
</div>
</Card.Footer>
</Card>
</form>
<CenteredPopup
show={umodalShow}
onHide={() => setUModalShow(false)}
title="Crop Image"
content=""
closetext="Close"
savetext="Apply"
onSaveChanges={onSaveImage}
>
<div className="cropimage-container">
<div className="cropimage-tools">
<span className="cropimage-icons" onClick={(e) => setCropShape("rect")}>
<MdOutlineCrop />
<span className="cropimage-text">Crop</span>
</span>
<span className="cropimage-icons" onClick={(e) => setCropShape("round")}>
<MdOutlineCircle />
<span className="cropimage-text">Circle</span>
</span>
</div>
<div className="crop-container">
<Cropper
image={Image}
crop={crop}
cropSize={cropSize}
cropShape={cropShape}
zoom={zoom || 1}
showGrid={true}
zoomWithScroll={false}
objectFit="horizontal-cover"
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={onCropperZoomChange}
/>
</div>
</div>
<div className="form-row ic_rangeslider">
<div className="form-group col-md-12">
<label className="form-label" htmlFor="ZoomBtn">
{zoom || 1}
</label>
<span className="range-slider__wrap">
<input
id="zoomrangeid"
type="range"
className="slider range-slider range-slider--light"
value={zoom || 1}
min={0.4}
max={1}
step={0.1}
aria-labelledby="Zoom"
onChange={onCropperZoomChange}
/>
</span>
</div>
</div>
</CenteredPopup>
</Page>
);
}
export default withRouter(EditImage);

Why do I have to refresh the page when I delete a post? MERN stack

I am a beginner in the MERN stack and I am interested in why I have to refresh the page after deleting the document (post)?
This is my Action.js
export const deletePost = id => async (dispatch, getState) => {
try {
dispatch({ type: DELETE_POST_BEGIN });
const {
userLogin: { userInfo },
} = getState();
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.delete(`/api/v1/post/${id}`, config);
dispatch({ type: DELETE_POST_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: DELETE_POST_FAIL,
payload: { msg: error.response.data.msg },
});
}
};
This is my Reducer.js
export const deletePostReducer = (state = {}, action) => {
switch (action.type) {
case DELETE_POST_BEGIN:
return { loading: true };
case DELETE_POST_SUCCESS:
return { loading: false };
case DELETE_POST_FAIL:
return { loading: false, error: action.payload.msg };
default:
return state;
}
};
And this is my Home page where i list all posts:
import { useEffect } from 'react';
import { Col, Container, Row } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { getPosts } from '../actions/postActions';
import Loader from '../components/Loader';
import Message from '../components/Message';
import Post from '../components/Post';
const HomePage = () => {
const dispatch = useDispatch();
const allPosts = useSelector(state => state.getPosts);
const { loading, error, posts } = allPosts;
const deletePost = useSelector(state => state.deletePost);
const { loading: loadingDelete } = deletePost;
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
return (
<Container>
{loading || loadingDelete ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<>
<Row>
{posts.map(post => (
<Col lg={4} key={post._id} className='mb-3'>
<Post post={post} />
</Col>
))}
</Row>
</>
)}
</Container>
);
};
export default HomePage;
And this is my single Post component:
const Post = ({ post }) => {
const dispatch = useDispatch();
const allPosts = useSelector(state => state.getPosts);
const { loading, error, posts } = allPosts;
const userLogin = useSelector(state => state.userLogin);
const { userInfo } = userLogin;
const handleDelete = id => {
dispatch(deletePost(id));
};
return (
<>
<div>{post.author.username}</div>
<Card>
<Card.Img variant='top' />
<Card.Body>
<Card.Title>{post.title}</Card.Title>
<Card.Text>{post.content}</Card.Text>
<Button variant='primary'>Read more</Button>
{userInfo?.user._id == post.author._id && (
<Button variant='danger' onClick={() => handleDelete(post._id)}>
Delete
</Button>
)}
</Card.Body>
</Card>
</>
);
};
And my controller:
const deletePost = async (req, res) => {
const postId = req.params.id;
const post = await Post.findOne({ _id: postId });
if (!post.author.equals(req.user.userId)) {
throw new BadRequestError('You have no permission to do that');
}
await Post.deleteOne(post);
res.status(StatusCodes.NO_CONTENT).json({
post,
});
};
I wish someone could help me solve this problem, it is certainly something simple but I am a beginner and I am trying to understand.
I believe the issue is that you are not fetching the posts after delete is successful.
Try this inside the HomePage component:
...
const [isDeleting, setIsDeleting] = useState(false);
const { loading: loadingDelete, error: deleteError } = deletePost;
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
useEffect(() => {
if (!deleteError && isDeleting && !loadingDelete) {
dispatch(getPosts());
}
setIsDeleting(loadingDelete);
}, [dispatch, deleteError, isDeleting, loadingDelete]);
...
Another method is to use "filtering", but you have to update your reducer as such:
export const deletePostReducer = (state = {}, action) => {
switch (action.type) {
case DELETE_POST_BEGIN:
return { loading: true };
case DELETE_POST_SUCCESS:
return { loading: false, data: action.payload}; // <-- this was changed
case DELETE_POST_FAIL:
return { loading: false, error: action.payload.msg };
default:
return state;
}
};
Now in your HomePage component, you will do something like this when rendering:
...
const { loading: loadingDelete, data: deletedPost } = deletePost;
...
useEffect(() => {
dispatch(getPosts());
if (deletedPost) {
console.log(deletedPost);
}
}, [dispatch, deletedPost]);
return (
...
<Row>
{posts.filter(post => post._id !== deletedPost?._id).map(post => (
<Col lg={4} key={post._id} className='mb-3'>
<Post post={post} />
</Col>
))}
</Row>
)

React fetched object not working the same after refactoring from class to function components

I'm making an React application that involves fetching an API. The code is below, but basically I'm fetching the data from the API and then mapping over it to present information about each character. It works with the class component, but I tried to change it to a function component and it no longer works. There is no error, it simply doesn't display anything.
From using console.log(), I know that I'm successfully getting the information into the "characters" variable from the API when using the function component, and from comparing the two applications and using console.log() on "this.state.characters" (class) and "characters" (function), I'm pretty sure that getting the exact same data, an array of like 660 of the characters from the show where each character's information is inside of an object. When I tried to add paragraph tags with {characters[0].name} inside of the function component, I got the error "TypeError: Cannot read property 'name' of undefined".
I don't know if it's because I messed up something stupid or because I don't understand something about some detail about the difference between class and function components, either is obviously very possible. Thank you for any help.
Here is the code from the class component:
import React, { Component } from 'react';
export class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
characters: [],
nameInput: '',
locationInput: '',
loading: true
};
};
async componentDidMount() {
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
for (let i = 1; i < 34; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
this.setState({characters: array, loading: false}, () => console.log(this.state.characters));
}
readInput = (e) => {
this.setState({nameInput: e.target.value});
console.log(this.state.nameInput);
}
readLocationInput = (e) => {
this.setState({locationInput: e.target.value});
console.log(this.state.locationInput);
}
render() {
return (
<div className="all">
<h4>Search by name:</h4>
<input onChange={this.readInput} />
<h4>Search by location:</h4>
<input onChange={this.readLocationInput} />
<br />
<div className="row m-1">
{this.state.loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : this.state.characters.filter((item) => {
if (this.state.nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(this.state.nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (this.state.locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(this.state.locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square">
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
)
})}
</div>
</div>
);
}
};
Here is the code from the function component:
import React, { Component, useEffect, useState } from 'react';
import logo from '../rickandmortylogo.png';
export const Body = () => {
const [characters, setCharacters] = useState([]);
const [nameInput, setNameInput] = useState('');
const [locationInput, setLocationInput] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
let url = 'https://rickandmortyapi.com/api/character/';
let array = [];
const fetchAPI = async () => {
for (let i = 1; i < 34; i++) {
let response = await fetch(url);
let data = await response.json();
for (let j = 0; j < 20; j++) {
array.push(data.results[j]);
}
url = data.info.next;
}
}
fetchAPI();
setCharacters(array);
setLoading(false);
}, []);
const readInput = (e) => {
setNameInput(e.target.value);
console.log(nameInput);
}
const readLocationInput = (e) => {
setLocationInput(e.target.value);
console.log(locationInput);
}
return (
<>
<div className="text-center">
<img src={logo} className="img-fluid" />
</div>
<h2>Click on a character here to add them to your favorites. Choose "Check Favorites" in the menu bar to see your favorites and "Search Characters" to come back.</h2>
<div className="all">
<h4>Search by name:</h4>
<input onChange={readInput} />
<h4>Search by location:</h4>
<input onChange={readLocationInput} />
<br />
<div className="row m-1">
{loading ? 'Loading can take a few seconds. Your Rick and Morty experience will be ready soon!' : characters.filter((item) => {
if (nameInput == "") {
return item;
} else {
if (item.name.toLowerCase().includes(nameInput.toLowerCase())) {
return item;
}
}
}).filter((item) => {
if (locationInput == "") {
return item;
} else {
if (item.location.name.toLowerCase().includes(locationInput.toLowerCase())) {
return item;
}
}
}).map((item, id) => {
return (
<>
<div className="col-md-4 border border-dark rounded" id="square">
<h2>{item.name}</h2>
<img src={item.image} className="border rounded" />
<h4>Location: {item.location.name}</h4>
<h4>Status: {item.status}</h4>
</div>
</>
);
})}
</div>
</div>
</>
);
};

React native error : undefined is not an object (evaluating 'this.props.navigator.push')

i am new in react native,i try to developing native application in react native but i have onPress click event issue.
InfoSwipper.js file i was click on Sign In button display "undefined is not an object(evaluating 'this.props.navigator.push') ". I am not sure what I am missing. please some one help me.
Thank you in advance.
index.android.js file code
"use strict";
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
StatusBar,
AsyncStorage,
} from 'react-native';
var SignUp = require("./components/SignUp");
var InfoScreens = require("./components/InfoScreens");
import SplashScreen from 'react-native-splash-screen'
var Kahaani = React.createClass({
_renderScene(route, navigator) {
if (route.id === 0) {
return <InfoScreens navigator={navigator} />
}
else if (route.id === 1) {
StatusBar.setHidden(false);
console.log('WRONG SIGNIN', "here");
return <LogIn navigator={navigator} />
}
},
componentDidMount() {
// do anything while splash screen keeps, use await to wait for an async task.
SplashScreen.hide();
},
render: function() {
return (
<Navigator
style={styles.navigationContainer}
initialRoute={{id: 0, }}
renderScene={this._renderScene} />
);
}
});
var styles = StyleSheet.create({
navigationContainer: {
flex: 1,
},
});
AppRegistry.registerComponent('Kahaani', () => Kahaani);
swipperScreen.js
'use strict';
import React, { Component } from 'react';
import {
Dimensions, // Detects screen dimensions
Platform, // Detects platform running the app
ScrollView, // Handles navigation between screens
StyleSheet, // CSS-like styles
View,
Text,Navigator, TouchableHighlight, // Container component
} from 'react-native';
import LogIn from './LogIn';
var navigator;
class InfoScreenSwiper extends Component {
constructor(props)
{
super(props);
}
static defaultProps = {
horizontal: true,
pagingEnabled: true,
showsHorizontalScrollIndicator: false,
showsVerticalScrollIndicator: false,
bounces: false,
scrollsToTop: false,
removeClippedSubviews: true,
automaticallyAdjustContentInsets: false,
index: 0
};
state = this.initState(this.props);
initState(props) {
const total = props.children ? props.children.length || 1 : 0,
index = total > 1 ? Math.min(props.index, total - 1) : 0,
offset = width * index;
const state = {
total,
index,
offset,
width,
height,
};
this.internals = {
isScrolling: false,
offset
};
return state;
}
onScrollBegin = e => {
// Update internal isScrolling state
this.internals.isScrolling = true;
}
onScrollEnd = e => {
// Update internal isScrolling state
this.internals.isScrolling = false;
// Update index
this.updateIndex(e.nativeEvent.contentOffset
? e.nativeEvent.contentOffset.x
// When scrolled with .scrollTo() on Android there is no contentOffset
: e.nativeEvent.position * this.state.width
);
}
onScrollEndDrag = e => {
const { contentOffset: { x: newOffset } } = e.nativeEvent,
{ children } = this.props,
{ index } = this.state,
{ offset } = this.internals;
if (offset === newOffset &&
(index === 0 || index === children.length - 1)) {
this.internals.isScrolling = false;
}
}
updateIndex = (offset) => {
const state = this.state,
diff = offset - this.internals.offset,
step = state.width;
let index = state.index;
if (!diff) {
return;
}
index = parseInt(index + Math.round(diff / step), 10);
this.internals.offset = offset;
this.setState({
index
});
}
swipe = () => {
if (this.internals.isScrolling || this.state.total < 2) {
return;
}
const state = this.state,
diff = this.state.index + 1,
x = diff * state.width,
y = 0;
this.scrollView && this.scrollView.scrollTo({ x, y, animated: true });
this.internals.isScrolling = true;
if (Platform.OS === 'android') {
setImmediate(() => {
this.onScrollEnd({
nativeEvent: {
position: diff
}
});
});
}
}
renderScrollView = pages => {
return (
<ScrollView ref={component => { this.scrollView = component; }}
{...this.props}
contentContainerStyle={[styles.wrapper, this.props.style]}
onScrollBeginDrag={this.onScrollBegin}
onMomentumScrollEnd={this.onScrollEnd}
onScrollEndDrag={this.onScrollEndDrag}
>
{pages.map((page, i) =>
// Render each slide inside a View
<View style={[styles.fullScreen, styles.slide]} key={i}>
{page}
</View>
)}
</ScrollView>
);
}
renderPagination = () => {
if (this.state.total <= 1) {
return null;
}
const ActiveDot = <View style={[styles.dot, styles.activeDot]} />,
Dot = <View style={styles.dot} />;
let dots = [];
for (let key = 0; key < this.state.total; key++) {
dots.push(key === this.state.index
// Active dot
? React.cloneElement(ActiveDot, { key })
// Other dots
: React.cloneElement(Dot, { key })
);
}
return (
<View
pointerEvents="none"
style={[styles.pagination, styles.fullScreen]}>
{dots}
</View>
);
}
onMainPressLogIn= () =>
{
console.log('WRONG SIGNIN', "onMainPressLogIn");
this.props.navigator.push({
id:1
});
}
onSubmitPressed () {
console.log('WRONG SIGNIN', "onSubmitPressed");
this.props.navigator.push({
id:2
})
}
renderButtonBottom = () => {
const lastScreen = this.state.index === this.state.total - 1;
return (
<View pointerEvents="box-none" style={[styles.buttonWrapper]}>
<View style={ [styles.viewButton]}>
<Text style={[styles.buttonLogIn,styles.buttonAll]} onPress={this.onMainPressLogIn} >LOG IN</Text>
<Text style={[styles.buttonSignUp,styles.buttonAll]} onPress={(this.onSubmitPressed.bind(this))}>SIGN UP</Text>
</View>
</View>
);
}
render = ({ children } = this.props) => {
return (
<View style={[styles.container, styles.fullScreen]}>
{/* Render screens */}
{this.renderScrollView(children)}
{/* Render pagination */}
{this.renderPagination()}
{/* Render Continue or Done button */}
{this.renderButtonBottom()}
</View>
);
}
}
module.exports = InfoScreenSwiper;

Resources