- 회원 관리 예제
- 장바구니 예제
- JS 예제 React 변환
1) 회원관리 예제
React를 이용해 회원 추가, 삭제, 수정 기능을 구현해 보았다.
import React, { useEffect, useRef } from 'react';
import { useState } from 'react';
import AddUser from './AddUser';
import EditUser from './EditUser';
import ListUser from './ListUser';
import Message from './Message';
const dataList = [
{ id: 1, name: '김', addr: '서울특별시' },
{ id: 2, name: '이', addr: '부산광역시' },
{ id: 3, name: '박', addr: '경기도 수원시' },
{ id: 4, name: '최', addr: '강원도 태백시' },
{ id: 5, name: '윤', addr: '경기도 성남시' },
]
const Customer = () => {
// localStorage.clear();
const [data, setData] = useState(
() => JSON.parse(localStorage.getItem('data')) || dataList
);
const no = useRef(data.length + 1)
const [isShow, setIsShow] = useState(false)
const [edit, setEdit] = useState({})
const [isMsg, setIsMsg] = useState(false)
const [msg, setMsg] = useState('')
useEffect(() => {
localStorage.setItem('data', JSON.stringify(data))
}, [data])
const onEdit = (item) => {
setIsShow(true)
setEdit(item)
}
const onDel = id => {
setData(data.filter(item => item.id !== id))
}
const onAdd = user => {
user.id = no.current++
setData([
...data,
user
])
setIsMsg(true)
setMsg('추가 완료')
}
const onUpdate = (user) => {
setData(data.map(item => item.id === user.id ? user : item))
}
return (
<div className='Customer'>
{
isShow ? <EditUser edit={edit} setIsShow={setIsShow} onUpdate={onUpdate} /> : <AddUser onAdd={onAdd} />
}
<Message msg={msg} isMsg={isMsg} setIsMsg={setIsMsg}/>
<ListUser data={data} onDel={onDel} onEdit={onEdit} />
</div>
);
};
export default Customer;
import React, { useRef, useState } from 'react';
const AddUser = ({ onAdd }) => {
const nameRef = useRef()
const [user, setUser] = useState(
{ name: '', addr: '' }
)
const { name, addr } = user
const changeInput = e => {
const { name, value } = e.target
setUser(
{
...user,
[name]: value
}
)
}
const onSubmit = e => {
e.preventDefault()
if (!name || !addr) return
onAdd(user)
setUser(
{
name: '',
addr: ''
}
)
nameRef.current.focus()
}
return (
<form onSubmit={onSubmit}>
<h2>회원 추가</h2>
<p>
<label>이름</label>
<input type="text" value={name} name="name" onChange={changeInput} ref={nameRef} />
</p>
<p>
<label>주소</label>
<input type="text" value={addr} name="addr" onChange={changeInput} />
</p>
<p>
<button type='submit'>회원추가</button>
</p>
</form>
);
};
export default AddUser;
회원 수정 버튼을 누르게 되면 해당 data를 edit input창에 보내주게 되는데 수정 화면에서 다른 회원의 수정 버튼을 누르면 나오지 않는다.
이때 사용해야 하는 것이 지난 시간에 배운 useEffect이다.
javascript에서 변수에 새로운 값이 할당될 때마다 재선언/재할당 해주는 것이랑 비슷하다.
따라서 edit값이 바뀔 때마다 useEffect로 인해 새로운 회원의 정보가 수정칸에 입력된다.
2) 장바구니 예제
import { useRef, useState } from 'react';
import CartForm from './CartForm';
import CartList from './CartList';
const Cart = () => {
const [data, setData] = useState([])
const [isEdit, setIsEdit] = useState(false)
const no = useRef(1)
const textRef = useRef('')
const [cart, setCart] = useState(
{ text: '', price: '', amount: '' }
)
const changeInput = e => {
const { name, value } = e.target
setCart(
{ ...cart, [name]: value }
)
}
const onSubmit = e => {
e.preventDefault()
if (!cart.text && cart.amount < 1) return
if (isEdit) {
cart.total = Number(cart.price * cart.amount)
const newData = data.map(item => item.id === cart.id ? { ...item, ...cart } : item)
setData(newData)
setCart(
{ text: '', price: '', amount: '' }
)
setIsEdit(false)
textRef.current.focus()
} else {
cart.id = no.current++
cart.total = Number(cart.price * cart.amount)
setData(
[...data, cart]
)
setCart(
{ text: '', price: '', amount: '' }
)
textRef.current.focus()
}
}
const onDel = id => {
setData(data.filter(item => item.id !== id))
}
const onDelAll = () => {
setData([])
}
const onEdit = (item) => {
setIsEdit(true)
setCart(item)
}
return (
<>
<div className="con-box">
<CartForm cart={cart} onSubmit={onSubmit} changeInput={changeInput} isEdit={isEdit} textRef={textRef} />
<CartList data={data} onDel={onDel} onDelAll={onDelAll} onEdit={onEdit} />
</div>
</>
);
};
export default Cart;
이번 예제에서 새로 배운 내용은 총금액에 해당하는 cart.total이다.
const onSubmit = e => {
e.preventDefault()
if (!cart.text && cart.amount < 1) return
if (isEdit) {
cart.total = Number(cart.price * cart.amount)
const newData = data.map(item => item.id === cart.id ? { ...item, ...cart } : item)
setData(newData)
setCart(
{ text: '', price: '', amount: '' }
)
setIsEdit(false)
textRef.current.focus()
} else {
cart.id = no.current++
cart.total = Number(cart.price * cart.amount)
setData(
[...data, cart]
)
setCart(
{ text: '', price: '', amount: '' }
)
textRef.current.focus()
}
}
form에서 submit을 할 때 price와 amount를 곱해서 나온 값을 숫자 형태로 변환(Number)하고 total에 넣어 주었다.
import CartItem from "./CartItem";
const CartList = ({ data, onDel, onDelAll, onEdit }) => {
return (
<div className="con2">
<p>
<button className="btn" onClick={onDelAll}>전체삭제</button>
<span className="total">
총금액 : {
data.reduce((acc, curr) => {
return acc + curr.total
}, 0).toLocaleString()
}
</span>
</p>
<ul className="list">
{
data.map(item => <CartItem key={item.id} item={item} onDel={onDel} onEdit={onEdit} />)
}
</ul>
</div>
);
};
export default CartList;
총금액의 경우 data객체의 total값을 다 더해야 하므로 reduce를 사용했다.
시작값은 0부터 누적해서 더하게 된다.
3) JS 예제 React로 변환
javascript로 만들었던 예제를 React로 변환해 보았다.
import React, { useRef, useState, useEffect } from 'react';
import './Basket.scss'
import '../assets/css/reset.css'
import BasketAdd from './BasketAdd';
import BasketList from './BasketList';
const Basket = () => {
const [data, setData] = useState([])
const titleRef = useRef('')
const [book, setBook] = useState(
{ title: '', author: '', bookcode: '' }
)
const no = useRef(1)
const [isMsg, setIsMsg] = useState(false)
const [isAddDel, setIsAddDel] = useState(false)
const [msg, setMsg] = useState('')
const onSubmit = (e) => {
e.preventDefault()
if (!book.title || !book.author || !book.bookcode) {
setIsMsg(true)
setIsAddDel(false)
setMsg('값을 입력하세요')
} else {
book.id = no.current++
setData(
[...data, book]
)
setBook(
{ title: '', author: '', bookcode: '' }
)
setIsMsg(true)
setIsAddDel(true)
setMsg('도서 추가 완료')
titleRef.current.focus()
}
}
const changeInput = (e) => {
const { name, value } = e.target
setBook(
{ ...book, [name]: value }
)
}
const onDel = (id) => {
setData(data.filter(item => item.id !== id))
setIsMsg(true)
setIsAddDel(true)
setMsg('도서 삭제 완료')
}
useEffect(() => {
const timer = setTimeout(() => {
setIsMsg(false)
setIsAddDel(false)
}, 2000);
return () => {
clearTimeout(timer)
}
}, [isMsg])
return (
<div>
<section className='main-book'>
<h2>북카페 도서 관리</h2>
<div className="inner">
<div className="book-input">
<BasketAdd onSubmit={onSubmit} book={book} changeInput={changeInput} titleRef={titleRef} />
</div>
<div className="book-list">
<BasketList data={data} onDel={onDel} />
</div>
</div>
{
isMsg ? <div className={`show ${isAddDel ? 'on' : 'off'}`}> {msg} </div> : <div></div>
}
</section>
</div>
);
};
export default Basket;
javascript로 만든 예제를 React로 구현할 때 어려웠던 부분은 연산자 제약이다.
비교적 적은 연산자만 사용 가능하기 때문에 '도서 추가 완료', '도서 삭제 완료', '값을 입력하세요' 총 3가지 메시지를 출력하기 위해 삼항연산자와 상태변수를 두 개 사용했다.
{
isMsg ? <div className={`show ${isAddDel ? 'on' : 'off'}`}> {msg} </div> : <div></div>
}
추가, 삭제, 값 미입력시 기본적으로 isMsg는 true가 되어야 하고, 추가와 삭제는 같은 on class를 사용하므로 isAddDel을 이용해 구별했다.
마지막으로 값 미입력시 나오는 메시지는 off 클래스를 붙여주기 위해 isMsg === true && isAddDel === false를 조건으로 만들어 주었다.
useEffect(() => {
const timer = setTimeout(() => {
setIsMsg(false)
setIsAddDel(false)
}, 2000);
return () => {
clearTimeout(timer)
}
}, [isMsg])
마지막으로 메시지를 2초 보여주기 위해서 isMsg의 상태가 변할 때 setTimeout이 실행되도록 구현했다.