Plus and Minus the Quantity of selected item only in the Cart and total price - React Redux - react-redux

I want to plus or minus the quantity of an item which is added in the cart. The issue is when I add another item to the cart and then when I plus or minus the quantity then the other item's quantity also changes. Also when quantity changes I want to add the total price of the quantity. I am using React Redux. Please help.
CART.JS
import React, { useState } from "react";
import "./cart.css";
import { useDispatch, useSelector } from "react-redux";
import {
clearCart,
minusQuantity,
plusQuantity,
removeFromCart,
} from "../../../redux/actions/CartActions";
const Cart = ({ id }) => {
const dispatch = useDispatch();
const product = useSelector((state) => state.addToCartReducer.products);
const qty = useSelector((state) => state.addToCartReducer.quantity);
// const minus = useSelector((state) => state.addToCartReducer.minus);
console.log(qty);
// console.log(minus);
const [quantity, setQuantity] = useState(1);
return (
<>
{product && (
<div
className={`dvCart ${id ? `col-lg-3` : `col-lg-2`} d-none d-lg-block`}
>
<div className="sticky-top" style={{ top: "90px" }}>
<div className="row">
<div className="col-sm-12 mb-1">
<h5 className="heading-5 d-inline-block mr-2">
Cart {product.length} items
</h5>
<span
className={`paragraph cp ${
product.length === 0 ? `d-none` : ``
}`}
onClick={() => dispatch(clearCart({}))}
>
clear all
</span>
</div>
{product && product.length === 0 ? (
<div className="dvCartEmpty col-12 d-none- mb-2">
<h5 className="heading-5 text-danger">Cart is Empty.</h5>
<p className="paragraph text-danger">
All Good No Bad! Go ahead, order some items from the menu.
</p>
{/* <!-- <img src="images/cart-empty.png" className="img-fluid" width="200" alt=""> --> */}
</div>
) : (
<div className="dvCartItems col-sm-12 mb-2">
<div className="row">
<div className="scrollbar mr-3">
{product.map((item) => {
const { id, heading, img, price, pack, size } = item;
return (
<div key={id} className="item col-sm-12 mb-2 pr-0">
<div className="bg-light pt-2 pb-2">
<div className="col-12">
<h5 className="heading-5">{heading}</h5>
<span className="paragraph mb-1 mr-2">
{size}
</span>
<span
className={`paragraph mb-1 ${
pack === `pack of 0` ? `d-none` : ``
}`}
>
{pack}
</span>
</div>
<div className="col-sm-12">
<div className="d-flex justify-content-between align-items-center">
<div className="addBtn d-flex justify-content-center align-items-center flex-1">
<div className="flex-1 text-center">
<i
className="fa fa-minus mr-1 cp p-1"
onClick={() =>
dispatch(
minusQuantity({ quantity: 1 })
)
}
></i>
</div>
<div className="flex-3 text-center">
<input
type="text"
value={qty}
onChange={(e) =>
setQuantity(e.target.value)
}
className="form-control text-center p-0"
/>
</div>
<div className="flex-1 text-center">
<i
className="fa fa-plus ml-1 cp p-1"
onClick={() =>
dispatch(
plusQuantity({ quantity: 1, id })
)
}
></i>
</div>
</div>
<div className="paragraph text-right">
<i className="fa fa-inr"></i>
{price}
</div>
</div>
</div>
<button
onClick={() =>
dispatch(
removeFromCart({ id, heading, price, size })
)
}
className="btn btn-remove"
>
<i className="fa fa-close"></i>
</button>
</div>
</div>
);
})}
</div>
</div>
</div>
)}
<div className="dvSubTotal col-sm-12 mb-2">
<div className="d-flex justify-content-between">
<div>
<h5 className="heading-5 text-success">Subtotal</h5>
</div>
<div>
<p className="heading-5 text-success">
<i className="fa fa-inr"></i>
{product
.map((item) => item.price)
.reduce((prev, curr) => prev + curr, 0)}
.00
</p>
</div>
</div>
<p className="paragraph">Extra charges may apply.</p>
</div>
<div className="dvProceed col-sm-12 mb-2">
<button
disabled={product.length !== 0 ? false : true}
className="btn btn-black w-100"
>
Proceed
</button>
</div>
</div>
</div>
</div>
)}
</>
);
};
export default Cart;
PRODUCTLIST.JS
import React, { useState } from "react";
import { Link } from "react-router-dom";
import "./productlist.css";
import ProductNotFound from "../../other/product-not-found/ProductNotFound";
import { useDispatch, useSelector } from "react-redux";
import { addToCart } from "../../../redux/actions/CartActions";
const ProductList = ({
id,
img,
pack,
price,
size,
heading,
description,
notfound,
}) => {
const dispatch = useDispatch();
// const [isDisabled, setIsDisabled] = useState(false);
const products = useSelector((state) => state.addToCartReducer.products);
// const isDisabled = useSelector((state) => state.addToCartReducer.isDisabled);
// console.log(isDisabled);
// console.log(products);
return (
<>
{notfound ? (
<ProductNotFound />
) : (
<div className="col-6 col-md-4 col-lg-6 col-xl-3 mb-4">
<div className="border border-light shadow-sm p-1 h-100">
<div className="image">
<p className={pack !== "pack of 0" ? "packs" : "d-none"}>
{pack}
</p>
<div className="bg-light text-center pt-2 pb-2 mb-1">
<Link className="d-inline-block" to={`/${id}`}>
<img src={img} className="img-fluid" alt={heading} />
</Link>
</div>
<h5 className="heading-5 text-center">{heading}</h5>
</div>
<div className="description d-flex justify-content-between mb-1">
<div className="paragraph">
<p>{size}</p>
</div>
<div className="paragraph mr-2">
<span>
<i className="fa fa-inr"></i>
<span>{price}</span>
</span>
</div>
</div>
<div className="addBtn text-center">
<button
onClick={() =>
dispatch(
addToCart({
id,
img,
pack,
price,
size,
heading,
description,
})
)
}
className="btn btn-white w-100"
disabled={products.find((item) => item.id === id) && true}
>
Add to Bag
</button>
</div>
</div>
</div>
)}
</>
);
};
export default ProductList;
CART ACTIONS.JS
import { actionTypes } from "../constants/action-types";
//ADD TO CART
export const addToCart = (item) => {
return {
type: actionTypes.ADD_TO_CART,
payload: item,
};
};
//PLUS QUANTITY
export const plusQuantity = (item) => {
return {
type: actionTypes.PLUS_QUANTITY,
payload: item,
};
};
//MINUS QUANTITY
export const minusQuantity = (item) => {
return {
type: actionTypes.MINUS_QUANTITY,
payload: item,
};
};
//REMOVE FROM CART
export const removeFromCart = (item) => {
return {
type: actionTypes.REMOVE_FROM_CART,
payload: item,
};
};
//CLEAR CART
export const clearCart = (item) => {
return {
type: actionTypes.CLEAR_CART,
payload: item,
};
};
CART REDUCER.JS
import { actionTypes } from "../constants/action-types";
const addToCartiState = {
products: [],
quantity: 1,
// minus: 0,
};
//CART REDUCER
export const addToCartReducer = (state = addToCartiState, action) => {
console.log(state);
console.log(action);
switch (action.type) {
case actionTypes.ADD_TO_CART:
return {
...state,
products: [...state.products, action.payload],
};
case actionTypes.PLUS_QUANTITY:
return {
...state,
//i am not sure if this logic is correct
quantity: state.quantity + action.payload.quantity,
};
case actionTypes.MINUS_QUANTITY:
return {
...state,
//i am not sure if this logic is correct
quantity:
state.quantity > 1 ? state.quantity - action.payload.quantity : 1,
};
case actionTypes.REMOVE_FROM_CART:
const filteredID = state.products.filter(
(item) => item.id !== action.payload.id
);
return {
...state,
products: filteredID,
};
case actionTypes.CLEAR_CART:
return {
...state,
products: [],
};
default:
return state;
}
};

Total cart quantity should be derived state and not stored in state. You should instead store an item quantity and compute a total quantity when rendering.
Add a quantity property when adding an item to the cart. If the item has already been added then simply update the quantity instead of adding a duplicate item.
Example item object:
{
id,
img,
pack,
price,
size,
heading,
description,
}
Cart reducer
const addToCartiState = {
products: [],
};
export const addToCartReducer = (state = addToCartiState, action) => {
switch (action.type) {
case actionTypes.ADD_TO_CART:
const isInCart = state.products.some(item => item.id === action.payload.id);
if (isInCart) {
// update existing item in cart
return {
...state,
products: state.products.map(
item => item.id === action.payload.id
? {
...item,
quantity: item.quantity + 1,
}
: item
),
};
}
// add new item to cart
return {
...state,
products: [
...state.products,
{
...action.payload,
quantity: 1
},
],
};
case actionTypes.PLUS_QUANTITY:
return {
...state,
products: state.products.map(
item => item.id === action.payload.id
? {
...item,
quantity: item.quantity + action.payload.quantity
}
:item
),
};
case actionTypes.MINUS_QUANTITY:
const item = state.products.find(item => item.id === action.payload.id);
if (item?.quantity === 1) {
// new quantity is 0, remove item from cart
return {
...state,
products: state.products.filter(item => item.id !=== action.payload.id),
};
}
// decrement quantity
return {
...state,
products: state.products.map(
item => item.id === action.payload.id
? {
...item,
quantity: item.quantity - action.payload.quantity
}
:item
),
};
case actionTypes.REMOVE_FROM_CART:
return {
...state,
products: state.products.filter(
(item) => item.id !== action.payload.id
),
};
case actionTypes.CLEAR_CART:
return {
...state,
products: [],
};
default:
return state;
}
};
Compute the derived cart total item quantity in the UI.
const Cart = ({ id }) => {
const dispatch = useDispatch();
const products = useSelector((state) => state.addToCartReducer.products);
const quantity = products.reduce((total, { quantity }) => total + quantity, 0);
console.log(quantity);

Related

GraphQL mutation on Form Click not triggering

I am trying to call Apollo Mutation Hook on form submit to create a new record in the database.
export default function NewJobForm() {
const { loading, error, data } = useQuery(GET_LOC_CAT_TYPE_QUERY);
const [newJob] = useMutation(NEW_JOB_MUTATION);
const [remote, setRemote] = useState(false);
const editorRef = useRef(null);
const log = () => {
if (editorRef.current) {
console.log(editorRef.current.getContent());
}
};
let initialFormValues = {
companyName: '',
title: '',
};
const JobSchema = yup.object({
companyName: yup.string().required('Please enter the company name.'),
title: yup.string().required('Please enter the job title.'),
});
let formValidate = (values) => {
console.log(values);
const errors = {};
return errors;
};
let handleError = (field, actions) => {
field.forEach((data) => {
let field = data.param;
switch (field) {
// case 'name':
// actions.setFieldError('name', data.msg);
// break;
default:
break;
}
});
};
let formSubmit = async (values, actions) => {
console.log(values);
newJob({ variables: values });
};
return (
<>
<Formik
validationSchema={JobSchema}
validate={(values) => formValidate(values)}
onSubmit={(values, actions) => formSubmit(values, actions)}
enableReinitialize={true}
initialValues={initialFormValues}
validateOnChange={true}
validateOnBlur={false}
>
{({ handleSubmit, setFieldValue, values }) => (
<form noValidate onSubmit={handleSubmit}>
<div className="space-y-8 divide-y divide-gray-200">
<div>
<div>
<h3 className="text-lg font-medium leading-6 text-gray-900">
Job Details
</h3>
<p className="mt-1 text-sm text-gray-500">
This information will be displayed
publicly so be careful what you share.
</p>
</div>
<div className="grid grid-cols-1 mt-6 gap-y-6 gap-x-4 sm:grid-cols-6">
<div className="sm:col-span-4">
<TextInput
label="Company Name"
id="companyName"
name="companyName"
type="text"
placeholder="eg: Google"
helpText="Please Enter the name of the company"
/>
</div>
<div className="sm:col-span-4">
<TextInput
label="Job Title"
id="title"
name="title"
type="text"
/>
</div>
</div>
</div>
</div>
</form>
)}
</Formik>
</>
);
}
On Form Submit the console.log displays the message but the mutation network call does not seem to get triggered.

How to Fetch the Data on Button Click using React Hooks

I want to display the Child component on click on Button, But here I am getting the Data when the page loads,I need to write a condition like when Button Clicked , then only the Child component show display otherwise a default text should display there.
const DetailsCard = () => {
const [employee, setemployee] = useState([])
const [data, setData] = useState()
useEffect(() => {
fetch("http://localhost:3000/users").then(res => res.json()).then((result) => {
setemployee(result);
}, [])
})
const handleChange = () => {
setData(true);
}
return (
<div>
<h1>LordShiva</h1>
<div className="container">
<div className="row">
<div className="col">
<div className="card">
<div className="card-header bg-primary text-white">
<p className="h4">Birthday APP</p>
<button className="btn btn-red true1 " onClick={handleChange} >HappyBirthday</button>
</div>
<div className="card-body">
<TableCard data={employee} />
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default DetailsCard;
const DetailsCard = () => {
const [employee, setemployee] = useState([]);
const [showChild, toggleShowChild] = useState(false);
useEffect(() => {
fetch("http://localhost:3000/users")
.then((res) => res.json())
.then((result) => {
setemployee(result);
}, []);
});
const handleChange = () => {
toggleShowChild(!showChild);
};
return (
<div>
<h1>LordShiva</h1>
<div className="container">
<div className="row">
<div className="col">
<div className="card">
<div className="card-header bg-primary text-white">
<p className="h4">Birthday APP</p>
<button className="btn btn-red true1 " onClick={handleChange}>
HappyBirthday
</button>
</div>
{/* Only show child when `showChild` is true. */}
{showChild && (
<div className="card-body">
<TableCard data={employee} />
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
};
export default DetailsCard;
Codesandbox
import React, { useState, useEffect } from "react";
const FetchAPI = () => {
const [data, setData] = useState([]);
const newData = data.slice(0, 12);
const [showData, setShowData] = useState(false);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos")
.then((response) => response.json())
// .then((json) => console.log(json));
.then((json) => setData(json));
}, []);
const onClickhandler = () => {
setShowData(!showData);
};
return (
<>
<h1>Search</h1>
<ul>
{newData.map((item) => {
return showData ? (
<li style={{ listStyle: "none", marginTop: "10px" }} key={item.id}>
{item.title}
</li>
) : (
""
);
})}
</ul>
<button onClick = {onClickhandler}>
Fetch
</button>
</>
);
};
export default FetchAPI;

Why the images of the publications are altered at the moment that I make a new publication with images?

when I reload browser I can see the post's pics
I have a component referring to the posts, but when you make a post that contains images, they are not updated and the post remains with the images from the previous post, the only way to prevent this from happening is by updating the browser and it should not be like that
Could someone help me to know what is happening, if I should update the component when loading the images or what should I do?
Thanks!
fomPost.vue =>
<template>
<div class="card bg-dark border-left-primary border-right-primary shd mb-4">
<div class="card-body">
<div v-if="status_msg" :class="{ 'alert-green': status, 'alert-danger': !status }" class="alert"
role="alert">{{ status_msg }}
</div>
<div class="form-group">
<textarea id="content-body" class="form-control" v-model="body"></textarea>
<a href="javascript:void(0)" class="text-lg float-right" data-toggle="tooltip"
data-title="Agregar imágenes" data-placement="bottom" #click="toggle()">
<i class="fas fa-cloud-upload-alt"></i>
</a>
</div>
<div v-show="isOpen" :style="'margin-top: 2rem'">
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
:class="{ dragging: isDragging }">
<div class="upload-control" v-show="images.length">
<label for="file">Agregar más imágenes</label>
</div>
<div v-show="!images.length">
<p>Arrastra las imágenes ó</p>
<div class="file-input">
<label for="file">Selecciónalas</label>
<input type="file" id="file" #change="onInputChange"
accept="image/x-png,image/gif,image/jpeg" multiple>
</div>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images" :key="index">
<div class="thumbnail" :style="`background-image: url(${image.replace(/(\r\n|\n|\r)/gm)})`">
<div class="options">
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Eliminar"
#click="removeimage(index)">
<i class="fas fa-trash-alt"></i>
</a>
</div>
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Previsualizar"
uk-toggle="target: #modal-media-image" #click="showImage(image)">
<i class="fas fa-search-plus"></i>
</a>
</div>
</div>
</div>
<div class="details">
<span class="size" v-text="getFileSize(files[index].size)"></span>
</div>
<div id="modal-media-image" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog uk-width-auto uk-margin-auto-vertical">
<button class="uk-modal-close-outside" type="button" uk-close></button>
<img width="1024px" :src="dialogImageUrl">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer bg-opacity-7-dark">
<button type="button" #click="createPost" class="btn btn-primary float-right">
<template v-if="isCreatingPost" class="align-items-center">
<div class="d-flex align-items-center">
<span class="mr-1">Publicando</span>
<div class="loadingio-spinner-dual-ring-botj7pu8xqc">
<div class="ldio-ifliw7yncz">
<div></div>
<div>
<div></div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
Publicar
</template>
</button>
</div>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
name: "FormPostText",
props: ['profile'],
data() {
return {
dialogImageUrl: "",
dialogVisible: false,
isDragging: false,
dragCount: 0,
files: [],
images: [],
status_msg: "",
status: "",
isCreatingPost: false,
title: "",
body: "",
isOpen: false
}
},
mounted() {
$("#content-body").emojioneArea({
placeholder: "¿Qué estás pensando?",
searchPlaceholder: "Buscar",
buttonTitle: "Usa la tecla [TAB] para insertarlos más rápido",
pickerPosition: 'bottom',
filtersPosition: "bottom",
searchPosition: "top"
});
},
methods: {
...mapActions({
create: "post/makePost"
}),
showImage(file) {
this.dialogImageUrl = file;
this.dialogVisible = true;
},
OnDragEnter(e) {
e.preventDefault();
this.dragCount++;
this.isDragging = true;
return false;
},
OnDragLeave(e) {
e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0)
this.isDragging = false;
},
onInputChange(e) {
const files = e.target.files;
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const files = e.dataTransfer.files;
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.showNotification("La imágen no puede ser mayor a 2MB.");
return;
}
if (!file.type.match('image.*')) {
this.showNotification(`${file.name} no es una imágen`);
return;
}
this.files.push(file);
const img = new Image(), reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
return isLt2M;
},
removeimage: function (index) {
this.images.splice(index, 1);
},
getFileSize(size) {
const fSExt = ['Bytes', 'KB', 'MB', 'GB'];
let i = 0;
while (size > 900) {
size /= 1024;
i++;
}
return `${(Math.round(size * 100) / 100)} ${fSExt[i]}`;
},
toggle() {
this.isOpen = !this.isOpen;
},
createPost() {
var body = $("#content-body").val();
var text = emojione.toImage(body);
if (!this.validateForm()) {
return false;
}
this.isCreatingPost = true;
const formData = new FormData();
formData.append("user", this.profile.uid);
formData.append("body", text);
this.files.forEach(file => {
formData.append('images[]', file, file.name);
});
this.create(formData).then((res) => {
if (res.data.status === 0) {
this.status = code;
this.showNotification(res.data.msg);
}
document.querySelector(".emojionearea-editor").innerHTML = '';
this.isCreatingPost = false;
this.images = [];
this.files = [];
this.isOpen = false;
let post = res.data;
this.$emit("new", post);
}).catch(error => {
console.log(error)
this.isCreatingPost = false;
});
},
validateForm() {
if (!$("#content-body").val()) {
this.status = false;
this.showNotification("Debes escribir algo para publicar...");
return false;
}
return true;
},
showNotification(message) {
this.$swal.fire({
icon: 'error',
html: message
});
}
}
}
</script>
Post.vue =>
<template>
<div id="timeline">
<div v-if="authenticated.username === username || isFriend">
<FormPost :profile="profile" #new="addPostText"></FormPost>
</div>
<!--<pre>{{postsArr}}</pre>-->
<div v-if="postsCount > 0">
<div v-for="(post, index) in postsArr" :key="index">
<div class="cardbox shadow-lg bg-opacity-5-dark shd">
<div class="cardbox-heading">
<div class="dropdown float-right">
<button class="btn btn-sm btn-dark btn-circle" data-toggle="dropdown"
data-boundary="window">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu dropdown-scale dropdown-menu-right" role="dropdown">
<template v-if="post.user.id === user.uid || post.friend.id === user.uid">
<b-dropdown-item href="javascript:void(0)" #click="deletePost(post.post.id, index)">
<span class="uk-margin-small-right" uk-icon="icon: trash"></span> Eliminar
</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
</template>
<b-dropdown-item href="javascript:void(0)">
<span class="uk-margin-small-right text-danger" uk-icon="icon: warning"></span>
Reportar
</b-dropdown-item>
</div>
</div>
<div class="media m-0">
<div class="d-flex mr-3">
<a v-bind:href="post.user.username">
<img class="img-fluid rounded-circle" v-bind:src="post.friend.photo" alt="User">
</a>
</div>
<div class="media-body">
<p class="m-0"><a v-bind:href="post.friend.username">{{ '#' + post.friend.username }}</a></p>
<small><span><i
class="far fa-clock"></i> {{ since(post.post.created_at) }}</span></small>
</div>
</div>
</div>
<div class="cardbox-item">
<p class="mx-4">{{ post.post.body | setEmoji }}</p>
<div v-if="post.images.length > 0">
<!--<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<img v-for="(imahe, index) in post.images" v-bind:src="`http://127.0.0.1:8000/storage/images/${post.friend.id}/${imahe.url}`" :key="index"/>
</photo-grid>-->
<ImagesGrid :images="post.images" :idFriend="post.friend.id"
uk-lightbox="animation: slide"></ImagesGrid>
</div>
</div>
<div class="cardbox-base">
<ul class="float-right">
<li><a><i class="fa fa-comments"></i></a></li>
<li><a><em class="mr-5">{{ post.comments_count || comments_count }}</em></a></li>
<li><a><i class="fa fa-share-alt"></i></a></li>
<li><a><em class="mr-3">03</em></a></li>
</ul>
<ul>
<li>
<Likes :postid="post.post.id" :user="user"></Likes>
</li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/3.jpeg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/1.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/5.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/2.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a><span>242 Likes</span></a></li>
</ul>
</div>
<CommentsPost
#new="commentsCount"
:postid="post.post.id"
:postuserid="user.uid"
:user="user"
></CommentsPost>
</div>
</div>
<nav class="pagination-outer">
<ul class="pagination">
<li class="page-item" :class="[pagination.current_page > 1 ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Previous" #click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item" v-for="page in pagesNumber" :class="[page == isActived ? 'active' : '']">
<a class="page-link" href="#" v-bind:data-hover="page" #click.prevent="changePage(page)">{{page}}</a>
</li>
<li class="page-item" :class="[pagination.current_page < pagination.last_page ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Next" #click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
<div v-else class="card bg-opacity-5-dark mb-4">
<div class="card-body">
<span class="text-light">No tiene posts aún.</span>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
var moment = require("moment");
moment.locale("es");
import {mapActions, mapGetters} from 'vuex'
import FormPost from "../Posts/FormPost";
import Likes from "../Posts/Likes";
import CommentsPost from "../Posts/CommentsPost";
import ImagesGrid from "../Posts/ImagesGrid";
export default {
name: "Posts",
props: {
username: {
type: String,
required: true
},
profile: {
type: Object,
required: true
},
user: {
type: Object,
required: true
},
isFriend: {
type: Boolean,
required: true
}
},
data() {
return {
page: 0,
offset: 4,
comments_count: ''
}
},
mounted() {
this.getPosts();
},
beforeRouteEnter(to, from, next) {
this.getPosts()
next()
},
beforeRouteUpdate(to, from, next) {
this.getPosts()
next()
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
postsArr: "post/postsLists",
pagination: "post/postsPagination",
postsCount: "post/postsCount"
}),
isActived: function() {
return this.pagination.current_page;
},
pagesNumber: function () {
if(!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if(from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if(to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
var pagesArray = [];
while(from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
}
},
methods: {
...mapActions({
getPostsByUser: "post/fetchPosts",
removePost: "post/deletePost",
}),
async getPosts(page) {
await this.getPostsByUser(page);
await this.$forceUpdate();
},
async addPostText(post) {
await this.postsArr.unshift(post);
await this.$forceUpdate();
await this.changePage(1);
},
since(d) {
return moment(d).fromNow();
},
commentsCount(count) {
this.comments_count = count;
},
deletePost(post, index) {
this.postsArr.splice(index, 1)
this.removePost(post)
},
changePage(page) {
this.pagination.current_page = page;
this.getPosts(page);
}
},
filters: {
setEmoji: (value) => {
var rx = /<img\s+(?=(?:[^>]*?\s)?class="[^">]*emojione)(?:[^>]*?\s)?alt="([^"]*)"[^>]*>(?:[^<]*<\/img>)?/gi;
return value.replace(rx, "$1")
}
},
components: {
FormPost,
Likes,
CommentsPost,
ImagesGrid
},
watch: {
'$route': 'getPosts'
}
ImagesGrid.vue =>
<template>
<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<a :href="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
v-for="(imahe, index) in images"
v-bind:data-image="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
:key="index">
</a>
</photo-grid>
</template>
<script>
export default {
props: ['idFriend', 'images'],
watch: {
$route(to, from, next) {
this.$forceUpdate();
next()
}
}
};
</script>
Post.js =>
import axios from 'axios'
import store from './index'
export default {
namespaced: true,
state: {
posts: [],
posts_count : 0,
pagination: {
'total': 0,
'current_page': 0,
'per_page': 0,
'last_page': 0,
'from': 0,
'to': 0,
}
},
getters: {
postsLists(state) {
return state.posts;
},
postsCount(state) {
return state.posts_count
},
postsPagination(state) {
return state.pagination
}
},
mutations: {
SET_POST_COLLECTION (state, data) {
state.posts = data.posts.data ;
state.pagination = data.paginate;
state.posts_count = data.paginate.total;
}
},
actions: {
makePost({commit,dispatch}, data) {
return new Promise((resolve, reject) => {
axios.post('user/post/create', data)
.then((response) => {
dispatch("fetchPosts", 1)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
fetchPosts({commit}, page) {
return new Promise((resolve, reject) => {
axios.get(`user/${store.state.user.profile.username}/posts?page=${page}`)
.then((response) => {
//console.log(response.data.posts.data);
commit('SET_POST_COLLECTION', response.data);
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
deletePost({commit}, id) {
return new Promise((resolve, reject) => {
let params = {'id': id};
axios.post('user/post/delete', params)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
createComment({commit}, comment) {
return new Promise((resolve, reject) => {
axios.post('user/post/comment/create', comment)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
}
}
}

How to Display search result on another page in react hooks

I m developing an e-commerce application with react hooks in search component result is shown beneath the component I want to render on search Result component how to redirect search result on the search Result component
import React, { useState, useEffect } from "react";
import { Link, Redirect } from "react-router-dom";
import { getCategories, list } from "./apiCore";
import SearchResult from "./SearchResult";
const Search = () => {
const [data, setData] = useState({
categories: [],
category: "",
search: "",
results: [],
searched: false,
});
const { categories, category, search, results, searched } = data;
const loadCategories = () => {
getCategories().then(data => {
if (data.error) {
console.log(data.error);
} else {
setData({ ...data, categories: data });
}
});
};
useEffect(() => {
loadCategories();
}, []);
const searchData = () => {
// console.log(search, category);
if (search) {
list({ search: search || undefined, category: category }).then(
response => {
if (response.error) {
console.log(response.error);
} else {
setData({ ...data, results: response, searched: true });
}
}
);
}
};
const searchSubmit = e => {
e.preventDefault();
searchData();
};
const handleChange = name => event => {
setData({ ...data, [name]: event.target.value, searched: false });
};
const searchMessage = (searched, results) => {
if (searched && results.length > 0) {
return `Found ${results.length} products`;
}
if (searched && results.length < 1) {
return `No products found`;
}
};
const searchedProducts = (results = []) => {
return (
<div>
<h2 className="text-muted mb-4">
{searchMessage(searched, results)}
</h2>
<div className="row">
{results.map((product, i) => (
<CarouselCard key={i} product={product} />
))}
</div>
</div>
);
};
return (
<div className="site-navbar-top">
<div className="container">
<div className="row align-items-center">
<div className="col-6 col-md-4 order-2 order-md-1 site-search-icon text-left">
<form className="site-block-top-search" onSubmit={searchSubmit}>
<span className="icon icon-search2"></span>
<input type="text" className="form-control border-0" placeholder="Search" onChange={handleChange("search")} />
</form>
</div>
<div className="col-12 mb-3 mb-md-0 col-md-4 order-1 order-md-2 text-center">
<div className="site-logo">
<Link to="/" className="js-logo-clone">Shoppers</Link>
</div>
</div>
<div className="col-6 col-md-4 order-3 order-md-3 text-right">
<div className="site-top-icons">
<ul>
<li><Link to="#"><span className="icon icon-person" /></Link></li>
<li><Link to="#"><span className="icon icon-heart-o" /></Link></li>
<li>
<Link to="cart.html" className="site-cart">
<span className="icon icon-shopping_cart" />
<span className="count">2</span>
</Link>
</li>
</ul>
</div>
</div>
</div>
</div>
{searchedProducts(results)}
</div>
)
}
export default Search;
The main part of the hook is pretty straightforward, but I'm having a difficult time finding a nice way to handle that redirect. The current working solution is to wrap the functional component with withRouter, then pass props.history

React TypeError: orders.map is not a function

I'm a begginer in React. I'm trying to fetch an array of data about orders and that is working and then map it to display specific information about each order.
I'm getting TypeError: orders.map is not a function exception in my application.
Here's my code:
class Orders extends Component {
state = {
orders: []
};
componentDidMount() {
axios
.get("https://localhost:9090/orders")
.then(res => {
this.setState({ orders: res.data });
console.log(res.data);
});
}
render() {
const { orders } = this.state;
const orderList =
this.state.orders.length > 0 ? (
orders.map(o => {
return (
<div key={o.orderId}>
<p>
{o.isbn}
</p>
</div>
);
})
) : (
<div className="row p-5 m-5">
<div className="offset-sm-5 col-sm-2 text-center">
<span className="text-grey r">Loading...</span>
</div>
</div>
);
return <div className="container">{orderList}</div>;
}}
What's interesting, I have a similar code, that is working. The only difference is basically what it's fetching. Here's the code:
class BookList extends Component {
state = {
books: []
};
componentDidMount() {
console.log(this.props.match.params.search_term);
axios
.get("https://localhost:8080/search?searchTerm=" + search_term)
.then(res => {
this.setState({ books: res.data });
console.log(res.data);
});
}
render() {
const { books } = this.state;
const booksList =
this.state.books.length > 0 ? (
books.map(b => {
return (
<div key={b.isbn} className="card">
<div className="card-body">
<h5 className="card-title">
<Link to={"/details/" + b.isbn}>{b.title}</Link>
</h5>
<div className="card-subtitle text-muted">
{b.author} ({b.year}) /{" "}
<span className=" text-danger">{b.category}</span>
</div>
<p />
</div>
</div>
);
})
) : (
<div className="row p-5 m-5">
<div className="offset-sm-5 col-sm-2 text-center">
<span className="text-grey r">Loading...</span>
</div>
</div>
);
return <div className="container">{booksList}</div>;
}}
I can't find the difference that would cause that exception. It is an array in both cases.
Any suggestions?
EDIT:
here's the output of response data:
response data of order
response data of bokstore
From them images it looks like the orders are processed as plain text and not parsed to JSON. Check that your back-end specifies the required headers Content-Type: application/json so that axios will parse the data correctly.
Alternatively you could parse the text client-side with JSON.parse(res.data)

Resources