I've been having an issue with react not rendering the updated cartItems in the state. The state updates perfectly fine but the items that I delete don't actually get removed from the rendering.
I'm very new to react, redux and state management but I assume nothing's actually wrong with the state itself, rather how/when the items are being iterated to be rendered. It's only when I refresh the page that it renders accurately.
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { Row, Col, ListGroup, Image, Button } from "react-bootstrap";
import Message from "../components/Message";
import { addToCart, removeFromCart } from "../actions/cartActions";
function Basket({ match, location, history }) {
const productId = match.params.id;
const qty = location.search ? Number(location.search.split("?")[1]) : 1;
const specID = location.search ? Number(location.search.split("?")[2]) : 0;
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
useEffect(() => {
if (productId) {
dispatch(addToCart(productId, qty, specID));
}
}, [dispatch, productId, qty, specID]);
const removeFromCartHandler = (cartItem) => {
dispatch(removeFromCart(cartItem));
};
const checkoutHandler = () => {
history.push("/login?redirect=shipping");
};
return (
<Row>
<Col md={8}>
{cartItems.length === 0 ? (
<Message variant="primary">
Your cart is empty{" "}
<Link to="/">
<strong>Go Back</strong>
</Link>
</Message>
) : (
<div>
<Link to="/">
<strong>Go Back</strong>
</Link>
<ListGroup variant="flush">
{cartItems.map((item) => (
<ListGroup.Item key={item.product} className="cart">
<Row>
{/* <Col xs={4} md={2}>
<Image src={item.img} alt={item.name} fluid rounded />
</Col> */}
<Col xs={4} md={3}>
<Link to={`/product/${item.product}`}>{item.name}</Link> (
{item.size})
</Col>
<Col xs={2} md={2}>
£{item.price}
</Col>
<Col xs={6} md={3}>
Qty
</Col>
<Col xs={6} md={1}>
<Button
type="button"
variant="light"
onClick={() => removeFromCartHandler(item)}
>
<i className="fas fa-trash" />
</Button>
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
</div>
)}
</Col>
</Row>
);
}
export default Basket;
The reducer for the cart.
import { CART_ADD_ITEM, CART_REMOVE_ITEM } from "../constants/cartConstants";
export const cartReducer = (
state = { cartItems: [{ specList: [{ price: [] }] }] },
action
) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
const existItem = state.cartItems.find(
(x) => x.product === item.product && x.size === item.size
);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x
),
};
} else {
return { ...state, cartItems: [...state.cartItems, item] };
}
case CART_REMOVE_ITEM:
return {
...state,
cartItems: state.cartItems.filter((x) => x !== action.payload),
};
default:
return state;
}
};
import axios from "axios";
import { CART_ADD_ITEM, CART_REMOVE_ITEM } from "../constants/cartConstants";
export const addToCart = (id, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/api/products/${id}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
});
localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
};
export const removeFromCart = (id) => async (dispatch, getState) => {
dispatch({
type: CART_REMOVE_ITEM,
payload: id,
});
localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
};
You are calling () => removeFromCartHandler(item) but at any other point assume the payload is id. You probably want to do () => removeFromCartHandler(item.id).
Related
I'm creating an e-commerce website and I've run into an issue that I'm having trouble solving. The functionality to add products to the cart, route to CartScreen, and be able to increase/decrease quantity and remove items from the cart all work. However, if there is an item in the cart and I want to add another, instead of having both items in the cart it replaces the first item with the second and copies its quantity. Does anyone have any idea how I can fix this?
Here is my Product.js:
import { useEffect } from "react";
import Card from 'react-bootstrap/Card';
import Button from 'react-bootstrap/Button';
import { Link } from 'react-router-dom';
import Rating from './Rating';
import axios from 'axios';
import { useContext } from 'react';
import { Store } from '../Store';
function Product(props) {
const { product } = props;
const { state, dispatch: ctxDispatch } = useContext(Store);
const {
cart: { cartItems },
} = state;
const addToCartHandler = async (item) => {
const existItem = cartItems.find((x) => x._id === product._id);
const quantity = existItem ? existItem.quantity + 1 : 1;
const { data } = await axios.get(`/api/products/${item._id}`);
if (data.countInStock < quantity) {
window.alert('Sorry. Product is out of stock');
return;
}
ctxDispatch({
type: 'CART_ADD_ITEM',
payload: { ...item, quantity },
});
};
return (
<Card>
<Link to={`/product/${product.slug}`}>
<img src={product.image} className="card-img-top" alt={product.name} />
</Link>
<Card.Body>
<Link to={`/product/${product.slug}`}>
<Card.Title>{product.name}</Card.Title>
</Link>
<Rating rating={product.rating} numReviews={product.numReviews} />
<Card.Text>${product.price}</Card.Text>
{product.countInStock === 0 ? (
<Button variant="light" disabled>
Out of stock
</Button>
) : (
<Button onClick={() => addToCartHandler(product)}>Add to cart</Button>
)}
</Card.Body>
</Card>
);
}
export default Product;
Here is my CartScreen.js:
import { useContext } from 'react';
import { Store } from '../Store';
import { Helmet } from 'react-helmet-async';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import MessageBox from '../components/MessageBox';
import ListGroup from 'react-bootstrap/ListGroup';
import Button from 'react-bootstrap/Button';
import Card from 'react-bootstrap/Card';
import { Link, useNavigate } from 'react-router-dom';
import axios from 'axios';
export default function CartScreen() {
const navigate = useNavigate();
const { state, dispatch: ctxDispatch } = useContext(Store);
const {
cart: { cartItems },
} = state;
const updateCartHandler = async (item, quantity) => {
const { data } = await axios.get(`/api/products/${item._id}`);
if (data.countInStock < quantity) {
window.alert('Sorry. Product is out of stock');
return;
}
ctxDispatch({
type: 'CART_ADD_ITEM',
payload: { ...item, quantity },
});
};
const removeItemHandler = (item) => {
ctxDispatch({ type: 'CART_REMOVE_ITEM', payload: item });
};
const checkoutHandler = () => {
navigate('/signin?redirect=/shipping');
};
return (
<div>
<Helmet>
<title>Shopping Cart</title>
</Helmet>
<h1>Shopping Cart</h1>
<Row>
<Col md={8}>
{cartItems.length === 0 ? (
<MessageBox>
Cart is empty. <Link to="/">Go Shopping</Link>
</MessageBox>
) : (
<ListGroup>
{cartItems.map((item, i) => (
<ListGroup.Item key={i}>
<Row className="align-items-center">
<Col md={4}>
<img
src={item.image}
alt={item.name}
className="img-fluid rounded img-thumbnail"
></img>{' '}
<Link to={`/product/${item.slug}`}>{item.name}</Link>
</Col>
<Col md={3}>
<Button
onClick={() =>
updateCartHandler(item, item.quantity - 1)
}
variant="light"
disabled={item.quantity === 1}
>
<i className="fas fa-minus-circle"></i>
</Button>{' '}
<span>{item.quantity}</span>{' '}
<Button
variant="light"
onClick={() =>
updateCartHandler(item, item.quantity + 1)
}
disabled={item.quantity === item.countInStock}
>
<i className="fas fa-plus-circle"></i>
</Button>
</Col>
<Col md={3}>${item.price}</Col>
<Col md={2}>
<Button
onClick={() => removeItemHandler(item)}
variant="light"
>
<i className="fas fa-trash"></i>
</Button>
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</Col>
<Col md={4}>
<Card>
<Card.Body>
<ListGroup variant="flush">
<ListGroup.Item>
<h3>
Subtotal ({cartItems.reduce((a, c) => a + c.quantity, 0)}{' '}
items) : $
{cartItems.reduce((a, c) => a + c.price * c.quantity, 0)}
</h3>
</ListGroup.Item>
<ListGroup.Item>
<div className="d-grid">
<Button
type="button"
variant="primary"
onClick={checkoutHandler}
disabled={cartItems.length === 0}
>
Proceed to Checkout
</Button>
</div>
</ListGroup.Item>
</ListGroup>
</Card.Body>
</Card>
</Col>
</Row>
</div>
);
}
This is my Store.js:
import { createContext, useReducer } from 'react';
export const Store = createContext();
const initialState = {
cart: {
cartItems: localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [],
},
};
function reducer(state, action) {
switch (action.type) {
case 'CART_ADD_ITEM':
// add to cart
const newItem = action.payload;
const existItem = state.cart.cartItems.find(
(item) => item._id === newItem._id
);
const cartItems = existItem
? state.cart.cartItems.map((item) =>
item._id === existItem._id ? newItem : item
)
: [...state.cart.cartItems, newItem];
localStorage.setItem('cartItems', JSON.stringify(cartItems));
return { ...state, cart: { ...state.cart, cartItems } };
case 'CART_REMOVE_ITEM': {
const cartItems = state.cart.cartItems.filter(
(item) => item._id !== action.payload._id
);
localStorage.setItem('cartItems', JSON.stringify(cartItems));
return { ...state, cart: { ...state.cart, cartItems } };
}
default:
return state;
}
}
export function StoreProvider(props) {
const [state, dispatch] = useReducer(reducer, initialState);
const value = { state, dispatch };
return <Store.Provider value={value}>{props.children} </Store.Provider>;
}
Before in the CartScreen I was getting an Error of child in the list should be a unique key prop. But after fixing the issue still remains so I guess that wasn't the issue.
i need to ask some question
I have 1 parent(index.js) and 2 childs (JobRequirement.js and JobList.js)
how can i send a data from job requirement to joblist?
here is my code
// JOBREQUIREMENT.JS
const JobRequirement = ({ isOpen, handleSelect }) => {
const [datas, setDatas] = useState([])
const loadData = () => {
axios.get('/bla')
.then((res) => {
setDatas(res.data.data)
})
}
useEffect(() => {
loadData()
}, [])
if (isOpen) {
return (
<Fragment>
<h5 className='mt-3 mb-2'>Job Requirements</h5>
<Row>
{datas.map((data) => (
<Col md='6' lg='4' key={data.id}>
<Card onClick={() => handleSelect(data.id)} className='text-center vertical-align-center mb-3'>
<CardBody>
<CardBody tag='h4'>{data.name}</CardBody>
</CardBody>
</Card>
</Col>
))}
</Row>
</Fragment>
)
} else return (<></>)
}
export default JobRequirement
// INDEX.JS
import { Fragment, useState } from 'react'
import JobRequirement from './JobRequirement'
import JobList from './JobList'
export default function JobRequirements() {
const [jobRequirement, setJobRequirement] = useState(true)
const [jobList, setJobList] = useState(false)
const selectJobRequirement = (data) => {
setJobRequirement(false)
setJobList(true, data)
}
const closeJobList = () => {
setJobRequirement(true)
setJobList(false)
}
return (
<div>
<Fragment>
<JobRequirement isOpen={jobRequirement} handleSelect={selectJobRequirement} />
<JobList isOpen={jobList} close={closeJobList} />
</Fragment>
</div>
)
}
// JOBLIST.JS
import { Fragment } from 'react'
import { Button, Row, Col, Card, CardBody } from 'reactstrap'
import { ChevronLeft } from 'react-feather'
const JobList = ({ isOpen, close, data }) => {
console.log(data)
if (isOpen) {
return (
<Fragment>
<div className='mt-2 mb-2'>
<Button size="sm" className='btn-icon' onClick={() => close()}><ChevronLeft size='15'/></Button> List of Job
</div>
<div>
<Row>
<Col md='6' lg='4'>
<Card className='text-center vertical-align-center mb-3'>
<CardBody>
<CardBody tag='h4'>Hello</CardBody>
</CardBody>
</Card>
</Col>
</Row>
</div>
</Fragment>
)
} else return (<></>)
}
export default JobList
I need to get a {data.id} from jobrequirement.js and use it in joblist.js
thankyou so much
I have been facing an issue with my code that whenever I add something to a cart it's being added but when am changing the quantity it adds another product with the same id and the new quantity amount even tho i have made a unique id and that's not working as it should be working:
Example of what am saying:
https://imgur.com/a/T1y1wdM
cartReducer.js
const initialState = {
cartItems: [],
};
export const cartReducer = (state = initialState, action) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
state.cartItems.push(item)
if (state.cartItems.length > 0) {
const existItem = state.cartItems.find(
(x) => x.id === item.id
);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((x) =>
x.id === existItem.id ? item : x
),
};
}
} else {
return {
...state,
cartItems: [...state.cartItems, item],
};
}
break;
default:
return state;
}
};
CartScreen.js
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
import { Link, useNavigate, useParams, useLocation } from "react-router-dom";
import {
Row,
Col,
ListGroup,
Image,
Form,
Button,
Card,
} from "react-bootstrap";
import { addToCart, editQty } from "../actions/cartActions";
const CartScreen = () => {
const productId = useParams();
const location = useLocation();
const qty = location.search ? Number(location.search.split("=")[1]) : 1;
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
useEffect(() => {
if (productId) {
dispatch(addToCart(productId, qty));
}
}, [dispatch, productId, qty]);
const removeFromCartHandler = (id) => {
console.log("rmeoved")
}
return (
<Row>
<Col md={8}>
<h1>Shopping Cart</h1>
{cartItems.length === 0 ? (
<Message>
Your cart is empty <Link to="/">Go back</Link>{" "}
</Message>
) : (
<ListGroup variant="flush">
{cartItems.map((item, index) => (
<ListGroup.Item key={index+1}>
<Row>
<Col md={2}>
<Image
src={item.image}
alt={item.name}
fluid
rounded
></Image>
</Col>
<Col md={3}>
<Link to={`/product/${item.id}`}>{item.name}</Link>
</Col>
<Col md={3}>
${item.price}
</Col>
<Col md={2}>
<Form.Control
as="select"
value={item.qty}
onChange={(e) => {
dispatch(addToCart(item, Number(e.target.value)));
}}
>
{[...Array(item.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))}
</Form.Control>
</Col>
<Col md={2}>
<Button type="button" variant="light" onClick={()=>{
removeFromCartHandler(item.product)
}}><i className="fa fa-trash"></i></Button>
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</Col>
<Col md={2}></Col>
<Col md={2}></Col>
</Row>
);
};
export default CartScreen;
cartActions.js
import { CART_ADD_ITEM, CART_FAIL_ITEM, CART_UPDATE_ITEM } from "../constants/cartConstants";
import { useParams, useLocation } from "react-router-dom";
export const addToCart = (id, qty) => async (dispatch, getState) => {
try {
const url = `/api/products/${id.id}`;
const { data } = await axios.get(url);
dispatch({
type: CART_ADD_ITEM,
payload: {
id: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
});
} catch (error) {
dispatch({
type: CART_FAIL_ITEM,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
}; ```
I am making a watchlist component using redux/react/hooks. Component is in 3 pieces (form to add to list, container to map over securities, and security to display each item)
So far I have been able to add values by dispatching addStock in the form component. I tried to do the same thing in security component for deleteStock but it isn't working/ reloads page.
reducer:
const stockSelector = (state = STOCK_STATE, action) => {
switch (action.type) {
case STOCK_SELECTED:
return action.payload;
case FETCH_STOCK_LIST:
return { ...state, watchlist: action.payload.select, title: action.payload.name, loading: false};
case STOCK_LIST_LOADING:
return {...state, loading: true}
case ADD_STOCK:
return { ...state, watchlist: [action.payload, ...state.watchlist] };
case DELETE_STOCK:
return {
...state,
watchlist: [
...state.watchlist.slice(0, action.payload),
...state.watchlist.slice(action.payload + 1)
],
};
default:
return state;
}
action
export const deleteStock = (payload) => ({
type: DELETE_STOCK,
payload,
});
watchlist component
const Watchlist = ({selected, watchlists, number}) => {
const dispatch = useDispatch();
const [taskList, setTaskList] = useState(['AAPL', 'MSFT', 'AMZN'])
const [list, setList] = useState(selected)
const [allList, setAllList] = useState(watchlists)
const [selectNumber, setSelectNumber] = useState(number)
const selectWatchlist = async () => {
setList(selected)
setSelectNumber(number)
}
useEffect(() => {
selectWatchlist()
.then(dispatch(fetchStockList(selectNumber)))
}, []);
return (
<React.Fragment>
<Col className="watchlist-master-col">
<Row className="watchlist-form-row">
<Col>
<AddWatchlistForm className="watchlist-form" />
</Col>
</Row>
<Row className="watchlist-list-row">
<ListSecurityContainer
list={taskList}
className="watchlist-list"
number={number}
/>
</Row>
<Row>
<Modal className='modalOne' />
</Row>
</Col>
<Modal />
</React.Fragment>
)
}
const mapStateToProps = (state) => {
console.log(state)
return {
selected: state.Watchlist.stock.title,
watchlists: state.Watchlist.watchlist.watchlist,
watchlist: state.Watchlist.stock.watchlist,
number: state.Watchlist.watchlist.number,
}
}
container
const ListSecurityContainer = ({loading, stocks}) => {
const dispatch = useDispatch();
const handleCloseTask = (id) => {
dispatch(deleteStock(id))
}
if (loading === false) {
return (
<React.Fragment>
<Col>
{stocks.map((value, index) => (
<Security
key={stocks[index]}
id={index}
{...value}
name={value}
// onClose={handleCloseTask}
className="security-elem"
/>
))}
</Col>
</React.Fragment>
);
}
return <div>Loading...</div>
}
const mapStateToProps = (state) => {
console.log(state.Watchlist.stock.watchlist)
return {
stocks: state.Watchlist.stock.watchlist,
loading: state.Watchlist.stock.loading
}
}
security
const Security = (value) => {
const dispatch = useDispatch();
const [taskName, setTaskName] =useState(value.name)
const removeTask = () => {
dispatch(deleteStock(taskName))
}
return (
<div className="list-group-item">
{value.name}
<button onClick={removeTask()} style={{ float: 'right' }}>
<i className="glyphicon glyphicon-remove"></i>
</button>
</div>
);
}
Fixed this by correcting the issues listed in the comments and also fixing a type that I had in my constants.js file.
const Security = ({index, name}) => {
const dispatch = useDispatch();
const [taskName, setTaskName] =useState(name)
const removeTask = (e) => {
e.stopPropagation()
dispatch(removeStock(index))
}
return (
<Row className="list-group-item">
<div className="item-titles">
{name}
</div>
<button onClick={() => dispatch(removeStock(index))} className="remove-item">
<i className="glyphicon glyphicon-remove"></i>
</button>
</Row>
);
}
Upon the click of a single like, it is increasing the number of likes for both separate components. What is causing both like numbers to increase, and how can I code it to where only one like number increases upon clicking a like?
I have also include the console in the picture below where I have console logged the logic in my reducer. You can find the code for the reducer further below the picture.
Reducer code
import { GET_GOALS, GOAL_ERROR, UPDATE_LIKES } from "../actions/types";
const initialState = {
goals: [],
goal: null,
loading: true,
error: {}
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_GOALS:
return {
...state,
goals: payload,
loading: false
};
case GOAL_ERROR:
return {
...state,
error: payload,
loading: false
};
case UPDATE_LIKES:
return {
...state,
goals: state.goals.map(goal =>
console.log("goal id", goal._id) === console.log("payload id", payload.goalId) ? { ...goal, likes: payload.likes } : goal
),
loading: false
};
default:
return state;
}
}
Action code
import axios from "axios";
import { GET_GOALS, GOAL_ERROR, UPDATE_LIKES } from "./types";
// Get goals
export const getGoals = () => async dispatch => {
try {
const res = await axios.get("/api/goal/goalfeed");
dispatch({
type: GET_GOALS,
payload: res.data
});
} catch (error) {
dispatch({
type: GOAL_ERROR,
payload: { msg: error.response }
});
}
};
// Add like
export const addLike = goalId => async dispatch => {
try {
const res = await axios.put(`/api/goal/like/${goalId}`);
dispatch({
type: UPDATE_LIKES,
payload: { goalId, likes: res.data }
});
} catch (error) {
dispatch({
type: GOAL_ERROR,
payload: { msg: error.response }
});
}
};
// Remove like
export const removeLike = goalId => async dispatch => {
try {
const res = await axios.put(`/api/goal/unlike/${goalId}`);
dispatch({
type: UPDATE_LIKES,
payload: { goalId, likes: res.data }
});
} catch (error) {
dispatch({
type: GOAL_ERROR,
payload: { msg: error.response }
});
}
};
Goals component code
import React, { useEffect } from "react";
import Moment from "react-moment";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { addLike, removeLike } from "../../actions/goal";
import { getGoals } from "../../actions/goal";
import Spinner from "../layout/Spinner";
import Navbar from "../dashboard/Navbar";
import ThumbUpAltIcon from "#material-ui/icons/ThumbUpAlt";
import ThumbDownAltIcon from "#material-ui/icons/ThumbDownAlt";
import ChatIcon from "#material-ui/icons/Chat";
import DeleteIcon from "#material-ui/icons/Delete";
import DoneIcon from "#material-ui/icons/Done";
import {
Typography,
Container,
CssBaseline,
makeStyles,
Grid,
Avatar,
Paper,
Button
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
paper: {
height: "auto",
marginBottom: theme.spacing(3)
},
actionButtons: {
marginTop: "3vh"
},
profileHeader: {
textAlign: "center",
marginBottom: 20
},
avatar: {
width: theme.spacing(7),
height: theme.spacing(7)
}
}));
const Goals = ({
getGoals,
auth,
addLike,
removeLike,
goal: { goals, user, loading }
}) => {
useEffect(() => {
getGoals();
}, [getGoals]);
const classes = useStyles();
return loading ? (
<>
<Navbar />
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Spinner />
</div>
</Container>
</>
) : (
<>
<CssBaseline />
<Navbar />
<main>
<Container>
<Typography variant="h2" className={classes.profileHeader}>
Goals
</Typography>
{/* parent grid */}
<Grid container spacing={4}>
{goals.map(singleGoal => (
<Grid
className={classes.paper}
key={singleGoal._id}
spacing={1}
container
item
direction="row"
alignItems="center"
component={Paper}
>
<Grid
item
container
direction="column"
justify="center"
alignItems="center"
xs={3}
>
<Avatar className={classes.avatar} src={singleGoal.avatar} />
<Typography variant="caption">
{singleGoal.first_name} {singleGoal.last_name}
</Typography>
<Typography variant="caption" className={classes.postedOn}>
Posted on{" "}
<Moment format="MM/DD/YYYY">{singleGoal.date}</Moment>
</Typography>
</Grid>
<Grid container item direction="column" xs={9}>
<Typography variant="body1">{singleGoal.text}</Typography>
<Grid item className={classes.actionButtons}>
<Button size="small" onClick={e => addLike(singleGoal._id)}>
<ThumbUpAltIcon />
</Button>
<Typography variant="caption">
{singleGoal.likes.length}
</Typography>
<Button
size="small"
onClick={e => removeLike(singleGoal._id)}
>
<ThumbDownAltIcon />
</Button>
<Button href={`/goal/${singleGoal._id}`} size="small">
<ChatIcon />
</Button>
{!auth.loading && singleGoal.user === auth.user._id && (
<Button size="small">
<DoneIcon />
</Button>
)}
{!auth.loading && singleGoal.user === auth.user._id && (
<Button size="small">
<DeleteIcon />
</Button>
)}
</Grid>
</Grid>
</Grid>
))}
</Grid>
</Container>
</main>
</>
);
};
Goals.propTypes = {
getGoals: PropTypes.func.isRequired,
goal: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
goal: state.goal,
auth: state.auth
});
export default connect(mapStateToProps, { getGoals, addLike, removeLike })(
Goals
);
There exists a flaw in your conditional test.
state.goals.map(goal =>
console.log("goal id", goal._id) === console.log("payload id", payload.goalId) // What is this? it will always evaluate to true
? { ...goal, likes: payload.likes }
: goal
)
console.log('EQUAL?', console.log() === console.log()); // true
console.log('EQUAL?', console.log(3) === console.log(3)); // true
console.log('EQUAL?', console.log(3) === console.log('three')); // true
console.log('EQUAL?', console.log('apple') === console.log({})); // true
console.log('EQUAL?', console.log(42) === console.log(-42)); // true
The function console.log is a void return, i.e. undefined, so you are comparing undefined === undefined, which is always true.
console.log(undefined === undefined); // true
You are spreading in the new 'likes' value to every goal object.
Try instead:
state.goals.map(
goal => goal._id === payload.goalId
? { ...goal, likes: payload.likes }
: goal
)