이전 포스팅에 cookies,SessionStorage,LocalStorage 를 봤다
이번에는 LocalStorage 를 활용해서 비회원장바구니 를 만들어보자!
기존 데이터를 불러와 map
으로 뿌려주고 장바구니 버튼을 만들어 브라우저에 저장하려고 한다.
주의 해야할점은 button 태그에 id 속성값을 사용하지 않는다 이유는 HTML은 텍스트 형식이기 때문에 객체가 들어갈수가 없다.
<button id={}>장바구니담기</button>
하지만 방법은 있다.
<button id={JSON.stringfy()}></button>
JSON.stringfy()
를 사용해 텍스트로 변경해서 사용하면된다.
하지만 여기서 하고자 하는건 HOF( Higher-order function )
을 이용해 함수에게 직접 데이터를 통째로 전달하고자 한다.
const saveBasket = (item: any) => () => {
localStorage.setItem("basket", item);
};
<button onClick={saveBasket(item)}>장바구니담기</button>
실제로 확인해보면 local Storage
에 [object Object]
형식으로 저장된다.
이유는 아까 처럼 텍스트만 들어갈수 있는데 객체의 형식으로 들어가게 되면 object 형식을 볼수 있다.
자 그러면 객체를 텍스트로 바꿔 넣어줘야한다.
const saveBasket = (item: any) => () => {
localStorage.setItem("basket", JSON.stringify(item));
};
필자는 필요한 부분만 가져오기 위해 rest파라미터를 사용했다.
const { __typename, ...NewItem } = item;
localStorage.setItem("basket", JSON.stringify(NewItem));
자 여기까지는 이제 브라우저에 원하는 데이터를 담을수 있게되었다.
하지만 지금은 장바구니를 담을때 마다 새로고침이 되고 있는데 추가하는 형식으로 하기 위해서는 이전에 저장되었던 데이터를 가져와서NewItem
과 합쳐야 한다.
const basket = JSON.parse(localStorage.getItem("basket") || "[]");
JSON.parse
로 객체 또는 배열로 만들어 선언한 변수에 값을 할당해주면된다.
오호 잘들어간다~✌🏻
이제 이전데이터 까지 모두 들어간다. 하지만 중복데이터 까지 들어가기 때문에 필터작업을 해줘야한다.
const basketTemp = basket.filter(
(basketItem: any) => basketItem._id === item._id
);
if (basketTemp.length === 1) {
alert("이미 장바구니에 담긴 상품입니다.");
return;
}
이미 담겨있는 아이템의 아이디와 마우스로 클릭하는 아이템의 id가 가 있는지 확인해주면 된다. 있다면 return
으로 함수를 종료 해주면 끝!✌🏻
전체코드
import { gql, useQuery } from "@apollo/client";
import styled from "@emotion/styled";
import { IUseditem } from "../../src/commons/types/generated/types";
export const FETCH_USED_ITEMS = gql`
query fetchUseditems($page: Int) {
fetchUseditems(page: $page) {
_id
name
contents
pricea
tags
images
useditemAddress {
address
createdAt
}
createdAt
}
}
`;
const MyRow = styled.div`
display: flex;
`;
const MyColumn = styled.div`
width: 25%;
`;
export default function BasketPage() {
const { data } = useQuery(FETCH_USED_ITEMS);
const saveBasket = (item: IUseditem) => () => {
const basket = JSON.parse(localStorage.getItem("basket") || "[]");
const basketTemp = basket.filter(
(basketItem: IUseditem) => basketItem._id === item._id
);
if (basketTemp.length === 1) {
alert("이미 장바구니에 담긴 상품입니다.");
return;
}
const { __typename, ...newItem } = item;
basket.push(newItem);
localStorage.setItem("basket", JSON.stringify(basket));
};
return (
<div>
{data?.fetchUseditems.map((item: IUseditem) => (
<MyRow key={item._id}>
<MyColumn>{item.name}</MyColumn>
<MyColumn>{item.price}</MyColumn>
<button onClick={saveBasket(item)}>장바구니담기</button>
</MyRow>
))}
</div>
);
}
useEffect(() => {
const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
setBesketItem(baskets);
},[]);
useEffect
를 사용해서 로컬스토리지를 찾아준다!
그리고 스테이트가 변경되면서 리렌더가 될거고 브라우저에 그려주게 된다.
useEffect
말고도 typeof window
를 사용해서 윈도우가 있는지 없는지로도 확인가능하다
if ( typeof window !== "undifined"){
const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
setBesketItem(baskets);
}
전체코드
import styled from "@emotion/styled";
import { useEffect, useState } from "react";
import { IUseditem } from "../../src/commons/types/generated/types";
const MyRow = styled.div`
display: flex;
`;
const MyColumn = styled.div`
width: 25%;
`;
export default function BasketPage() {
const [basketItem, setBesketItem] = useState([]);
// if ( typeof window !== "undifined"){
// const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
// setBesketItem(baskets);
// }
useEffect(() => {
// 로컬스토리지를 찾기 위해 유즈 이펙트를 사용한다
const baskets = JSON.parse(localStorage.getItem("basket") || "[]");
setBesketItem(baskets);
},[]);
return (
<div>
{basketItem.map((el: IUseditem) => (
<MyRow key={el._id}>
<MyColumn>{el.name}</MyColumn>
<MyColumn>{el.price}</MyColumn>
</MyRow>
))}
</div>
);
}