React - Store data in localStorage after click on Icon - reactjs

I'm trying to save data on localStorage if the user clicked on a like button and remove it if the user unclicked it. This is my code
const [menu, setMenu] = useState([]);
const [iconStates, setIconStates] = useState({});
...
...
...
{recipes.reduce(reduceRecipes, []).map((item, index) => (
<Carousel.Item key={item._id}>
<div className="d-flex justify-content-center">
<Row>
{item.map((item, index) => {
return (
<Col sm>
<Card key={item._id} style={{ width: "18rem" }}>
<Card.Img variant="top" src={item.photo_location} />
<Card.Body>
<div className="title-container">
<Card.Title>
{item.name}
<RiHeart3Fill
className={
iconStates[item._id] ? 'heart active' : 'heart'
}
onClick={() => {
if (menu.includes(item)) {
setMenu(menu.filter(menuItem => menuItem._id !== item._id));
} else {
setMenu(menu.concat(item));
}
localStorage.setItem('menu', JSON.stringify(menu));
setIconStates({...iconStates, [item._id]: !iconStates[item._id]})
}}
/>
I Codesandbox version is here.
It doesn't work as it supposed to do now, when I click the icon on one item it doesn't get added until I click it again, and if I unclicked any item everything in the LocalStorage is deleted. can anyone elaborate on how to fix this?

Related

react - have to click twice to change useState value

I've seen many answers to this question but I can't make it work for me, I'm trying to have an Add to Menu button for each recipe that I have, and what happens now is that on the first click it creates an empty array, then the second time it works.
const [selectedItems, setSelectedItems] = useState([]);
const handleClick = (e,selectedItem) => {
let newState = [...selectedItems,selectedItem]
setSelectedItems(newState);
console.log(selectedItems)
}
...
...
...
{recipes.reduce(reduceRecipes, []).map((item, index) => (
<Carousel.Item key={item._id}>
<div className="d-flex justify-content-center">
<Row>
{item.map((item, index) => {
return (
<Col sm>
<Card key={item._id} style={{ width: "18rem" }}>
<Card.Img variant="top" src={item.photo_location} />
<Card.Body>
<div className="title-container">
<Card.Title>
{item.name}
</Card.Title>
<p><FcAlarmClock/> {item.prep_time + item.cook_time} minutes</p>
</div>
<Button variant='warning' onClick={(e) => {handleClick(e,item)}}>Add to Menu</Button>
</Card.Body>
</Card>
</Col>
);
})}
</Row>
</div>
</Carousel.Item>
))}
The update will be reflected in the next render. That's how react works by design.
Take your example:
const handleClick = (e,selectedItem) => {
console.log(selectedItems) // current state
let newState = [...selectedItems,selectedItem]
setSelectedItems(newState);
console.log(selectedItems) // This won't print the recently updated state, but the same it printed at the beginning of the function
}
Check this: https://stackoverflow.com/a/54069332/4898348
It's unclear why you need selectedItems to have the updated state right there. You can just simply use newState instead.

How can I add item on localStroge through ReactJS

I don't understand how I can add an item on localStroge with the handle change method in react js.
The problem is I want to add a favorite list. When I click the favorite list all checkbox fill I need to control them.
I want to store the filled item in my local storage. handlechange function fill all favorite icon why?
Every click will be a single item fill. Is it possible to Material UI or Martial UI checkBox icon? How can I handle it?
Here is my UI view
function Main() {
// Here is my state define
const [checked, setChecked] = React.useState(
localStorage.getItem("love") === "true"
);
const handleChange = (e) => {
localStorage.setItem("love", `${e.target.checked}`);
setChecked(e.target.checked);
console.log(e.target.checked);
};
return (
<>
<div className="row mt-5">
{isLoading ? (
<>
{Array.from({ length }).map((_, i) => (
<MainLoading key={i} />
))}
</>
) : error ? (
<p>Error occured</p>
) : (
<>
{data?.map((product) => (
<div className="col-md-3 mb-5 text-center" key={product.id}>
<img
className="w-100"
style={{ height: "200px", objectFit: "contain" }}
src={product.image}
alt=""
/>
<div>{product.title.substring(0, 20)}</div>
<button
onClick={() => handelAddTocard(product)}
className="mt-3"
>
Add to card
</button>
<button>
<Link
to={`/details/${product.id}`}
className="mt-3 text-decoration-none text-black"
>
view details
</Link>
</button>
{/* How can i control evey single item */}
<Checkbox
checked={checked}
onChange={handleChange}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>
</div>
))}
</>
)}
</div>
</>
);
}
export default Main;
The problem is that you are using a boolean and you have no way to identify a specific item.
If you want to favorite multiple items, I would use something like this:
const [checked, setChecked] = React.useState(
JSON.parse(localStorage.getItem("loveIds") || "[]")
);
const handleCheck = (id, productChecked) => {
const newItems = productChecked ? [...checked, id] : checked.filter(x => x !== id);
localStorage.setItem("loveIds", JSON.stringify(newItemS));
setChecked(newItems);
console.log(newItems);
};
// ...
<Checkbox
checked={checked}
onChange={(e) => handleCheck(product.id, e.target.checked)}
icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
/>

Images not displaying in the react.js

I am trying to render the images in React.js as a card content but it does not show the image.
The code to display image:
const Template = ({ template }) => {
console.log(template.preview_image) // <- It displays the image path
return (
<Card className="my-3 p-3 rounded">
<Link to={`/template/${template._id}`}>
<Card.Img src={template.preview_image} variant="top" />
</Link>
<Card.Body>
<Link to={`/template/${template._id}`}>
<Card.Title as="div">
<strong>{template.name}</strong>
</Card.Title>
</Link>
</Card.Body>
</Card>
);
};
The template prop coming from the following code:
{loading ? (
<Loader />
) : error ? (
<Message variant="danger">{error}</Message>
) : (
<Row>
{templates &&
templates.map((template) => (
<Col key={template._id} sm={12} md={6} lg={4} xl={3}>
<Template template={template} />
</Col>
))}
</Row>
)}
The useEffect function:
const templateList = useSelector((state) => state.templateList);
const { error, loading, templates } = templateList;
useEffect(() => {
dispatch(listTemplates());
}, [dispatch]);
The images are also saved at the server side in the uploads folder.
Am I missing something?

How to make links work with card-img-overlay React

I'm having an issue on my project. I created a card-img-overlay to display icons over an image. If you click on the entire image you are redirected to a post. I would like to make the like and share icons clickable.
My project is in Reactjs. I am displaying images and videos from Reddit API.
Thank you for your help.
id,
slugTitle,
title,
url_overridden_by_dest,
author,
preview,
}) => {
const [isVideo, setIsVideo] = useState(false);
useEffect(() => {
if (preview) setIsVideo(preview.split('.').pop() === 'mp4');
}, [preview]);
const history = useHistory();
const goToPage = () => {
history.push(`/Post/${id}/${slugTitle}`);
};
return (
<Card
inverse
onClick={goToPage}
style={{
cursor: 'pointer',
}}
>
{isVideo && (
<video autoPlay="false" loop width="100%" src={preview}>
<track default kind="captions" />
</video>
)}
{!isVideo && (
<CardImg top width="100%" src={url_overridden_by_dest} alt={title} />
)}
<CardImgOverlay className="hideinfos">
<CardText className="w-100 d-flex justify-content-between">
<div>
<VscAccount className="mr-2" size={20} />
{author}
</div>
<div>
<LikeButtonhp
className="mr-2 card-link"
size={20}
style={{
position: 'relative',
}}
/>
<BiShareAlt size={20} />
</div>
</CardText>
</CardImgOverlay>
</Card>
);
};
You'll need to put onClick handlers on your LikeButtonhp and BiShareAlt components, and use event.stopPropagation() to stop the event from bubbling up to the <Card />:
<BiShareAlt
size={20}
onClick={event => {
event.stopPropagation();
// Do stuff for share click
}}
/>
You may need to alter the BiShareAlt and LikeButtonhp components to support an onClick prop also, for example if they render a <button> element it may look like this:
const BiShareAlt = ({ onClick }) => (
<button onClick={onClick}>
Share
</button>
);
export default BiShareAlt;
In my onClick, I added an e.stopPropagation(); and it solves my problem. Now I can click on the heart icon and it works. It stops the onClick set up on my image (parent).
function LikeButtonhp() {
const [liked, setLiked] = useState(false);
return (
<Button
outline
color="link"
className="likebutton"
onClick={(e) => {
e.stopPropagation();
setLiked(!liked);
}}
style={{ color: 'white' }}
>
{liked ? <BsHeartFill size={20} /> : <BsHeart size={20} />}
</Button>
);
}

Delete the user from the List : Ant Design

Can somebody help me to delete data of user list
Code is available below https://codesandbox.io/s/r4try
Please tell me how to delete particular user from the user list. please use delete button on user
here is the solution
Codesandbox Demo
https://codesandbox.io/s/control-between-forms-ant-design-demo-99sus?file=/index.js
<Form.Item
label="User List"
shouldUpdate={(prevValues, curValues) => prevValues.users !== curValues.users}
>
{({ getFieldValue, setFieldsValue }) => {
const users = getFieldValue('users') || [];
return users.length ? (
<ul>
{users.map((user, index) => (
<li key={index} className="user">
<Avatar icon={<UserOutlined />} />
{user.name} - {user.age}
<CloseOutlined onClick={() =>{
const updatedUsers = delete users[index];
setFieldsValue({users: updatedUsers})
}} style={{ paddingLeft: 15 }}/>
</li>
))}
</ul>
) : (
<Typography.Text className="ant-form-text" type="secondary">
( <SmileOutlined /> No user yet. )
</Typography.Text>
);
}}
</Form.Item>

Resources