Modal.js
파일 안에서 Modal
BackDrop
컴포넌트를 만든다.
모달컴포넌트 안에 props.children
을 넣어준다.
index.html
의 최상위 태그를 만들어준다. (id=overlay)
createPortal
을 사용한 코드는 다음과 같다.
//Modal.js
import React from "react";
import classes from "./Modal.module.css";
import reactDom from "react-dom";
const BackDrop = () => {
return <div className={classes.backdrop}></div>;
};
const Modal = (props) => {
const overlayPosition = document.getElementById("overlay");
return (
<>
{reactDom.createPortal(<BackDrop />, overlayPosition)}
{reactDom.createPortal(
<div className={classes.modal}>{props.children}</div>,
overlayPosition
)}
</>
);
};
export default Modal;
store
이라는 폴더를 만들고 cart-context.js
파일을 만든다.
cart-context
는 공유할 변수와 함수를 담는 곳이다.
import React from "react";
const CartContext = React.createContext({
item: [],
totalAmount: 0,
addItem: (item) => {},
removeItem: (id) => {},
});
export default CartContext;
CartProvider
컴포넌트를 만든다.
cartContext
는 업데이트 되는 값들을 집어넣는 곳이다. 나중에 동적으로 할당해주기 위해 더미 데이터를 집어넣는다.
꼭 value
props로 업데이트 되는 값들을 집어넣어준다.
//CartProvider.js
import React from "react";
import CartContext from "./cart-context";
const CartProvider = (props) => {
const addItemToCartHandler = (item) => {};
const removeItemFromCartHandler = (item) => {};
const cartContext = {
item: [],
totalAmount: 0,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};
return (
<CartContext.Provider value={cartContext}>
{props.children}
</CartContext.Provider>
);
};
export default CartProvider;
데이터를 사용하고 싶은 장소에서 import { useContext } from "react";
import CartContext from "../../store/cart-context";
두개를 import 해준다.
const ctx = useContext(CartContext);
로 변수를 사용한다.
장바구니에 있는 모든 물건의 갯수를 세기 위해서 필요한 빌트인 함수
item
의 모든 요소를 돌면서 현재 있는 값cur
에 item.amount
를 더한 것을 리턴한다.
const totalNumber = ctx.item.reduce((cur, item) => {
return cur + item.amount;
}, 0)
import React, { useReducer } from "react";
해준다.
컴포넌트 안에 기본적인 useReducer
문을 적는다.
const [cartState, dispatchCart] = useReducer(cartReducer, {
defaultCartState,
});
const defaultCartState = { item: [], totalAmount: 0 };
디폴트 값이다.addItemToCartHandler
과 removeItemFromCartHandler
에서 dispatchCart
를 실행시킨다. const cartContext = {
item: cartState.item,
totalAmount: cartState.totalAmount,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};
action
으로 다음의 인자가 전달이 되는데 전달받은 곳에서 cartReducer
함수가 실행된다. const addItemToCartHandler = (item) => {
dispatchCart({
type: "ADD",
item: item,
});
};
const removeItemFromCartHandler = (id) => {
dispatchCart({ type: "REMOVE", id: id });
};
concat
이라는 자바스크립트 빌트인 함수를 사용했는데 push
를 사용하지 않은 이유는 concat
이 새로운 배열을 만들기 때문이다.(push
는 수정함)const cartReducer = (state, action) => {
if (action.type === "ADD") {
const newitems = state.item.concat(action.item);
const newAmount =
state.totalAmount + action.item.price * action.item.amount;
return {
item: newitems,
totalAmount: newAmount,
};
} else if (action.type === "REMOVE") {
}
return defaultCartState;
};
커스텀 컴포넌트에서는 ref의 기능을 사용할 수 없다.
따라서<INPUT>
컴포넌트 안에 있는<input>
에서forwardRef
를 사용해야 한다.
React.forwardRef
으로 감싸주고 <input>
에 ref
을 추가해준다.import React from "react";
import classes from "./Input.module.css";
const Input = React.forwardRef((props, ref) => {
return (
<div className={classes.input}>
<label htmlFor={props.id}>{props.label}</label>
<input {...props.input} ref={ref} />
</div>
);
});
export default Input;
상위 컴포넌트로 가서 useRef
을 import
해준다.
const amountData = useRef();
선언후 amount.current.value
로 사용한다.
특정 조건 (Cart에 음식을 담을 때)에서 에니메이션을 추가하는 방법
useState
를 사용해서 에니메이션을 진행할지 안할지에 대한 변수를 선언한다. const [isBump, setIsBump] = useState(false);
클래스를 동적으로 추가한다. const bumpClasses = `${classes.button} ${isBump ? classes.bump : ""}`;
useEffect
와 구조분해 할당을 이용해서 클래스를 계속 추가했다가 빼준다.
const { item: newCtxItem } = ctx;
useEffect(() => {
if (newCtxItem.length < 1) return;
setIsBump(true);
const timer = setTimeout(() => {
setIsBump(false);
}, 300);
return () => {
clearTimeout(timer);
};
}, [newCtxItem]);