
fetchBoards 로 data 를 받아오고, 이를 map 함수로 뿌린 다음 버튼을 클릭하면 해당 요소 (el) 를 받을 수 있는 버튼 onClickBasket 함수를 만듬// 비회원 장바구니에 클릭한 게시글을 넣어주는 함수
const onClickBasket = (el: IBoard) => () => {
const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
const temp = baskets.filter((basketEl: IBoard) => basketEl._id === el._id);
if (temp.length === 1) {
alert("이미 담으신 물품입니다!!!");
return;
}
const { __typename, ...newEl } = el;
baskets.push(newEl);
localStorage.setItem("baskets", JSON.stringify(baskets));
};
기존에 로컬스토리지에 저장된 데이터가 있다면 (장바구니에 담은 적이 있다면) 해당 데이터를 불러오고, 그렇지 않으면 빈 배열("[ ]")을 불러옴
if 조건문을 통해 filter로 현재 선택한 (el)의 id와 배열 안에 들어있는 el들의 id 값을 비교하여 같은 것이 있다면(이미 선택한 적이 있어서 데이터에 저장되어 있다면) 알림창을 띄우고 함수 종료
rest 메소드를 활용해 받아오는 (el) 에서 필요없는 key와 value를 제거해 줌
이후 push 로 정리된 newEl을 baskets 안에 넣어주고 해당 baskets을 localstorage.setItem 으로 로컬스토리지에 저장
*localstorage 에는 문자열만 저장되기 때문에 배열, 객체로 들어오는 값은 꼭 JSON.stringify, JSON.parse 사용해서 변환해줘야 함!!
+a) 비회원 장바구니 조회 기능 구현
import { useEffect, useState } from "react";
import { IBoard } from "../../src/commons/types/generated/types";
export default function BasketLoggedInPage() {
const [basketItems, setBasketItems] = useState([]);
useEffect(() => {
const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
setBasketItems(baskets);
}, []);
return (
<div>
<h1>나만의 장바구니(비회원전용!!)</h1>
{basketItems.map((el: IBoard) => (
<div key={el._id}>
<span>{el.writer}</span> |
<span>{el.title}</span>
</div>
))}
</div>
);
}
localStorage.getItem 으로 불러와서 변수에 담은다음 map 함수로 화면에 뿌려줄 수 있음
*이 때 그냥 렌더링 시 화면에 표출되게 하면 프론트 서버에서 프리렌더링 하는 동안 localStorage를 읽지 못해 [undefined] 에러가 발생함으로 useEffect 함수 안에 넣어서 렌더링이 완료된 이후 불러올 수 있도록 함!!
react-hook-form 을 사용하면 함수를 일일히 만들어 바인딩해 줄 필요 없이 간편하게 코딩 가능 (노가다성 코딩 필요 X)
ex. 기존에 setState를 이용해 각 input의 입력값을 받아오던 것을 라이브러리로 한 번에 해결 가능
폼 라이브러리 종류 = [react-form, redux-form, react-hook-form, formik...]

반면에 react-hook-form 은 각 값을 입력받고 등록된 함수가 실행 될 때 한 번에 처리하기 때문에 불필요한 렌더링이 없고 효율적임
*이렇게 한 번에 처리하는 방식을 비제어 컴포넌트라고 함
*다만 react-hook-form 의 default 가 비제어 방식일 뿐, 설정에서 mode : "onChange"로 변경하여 제어 컴포넌트 방식으로 사용할 수도 있음
[비제어 컴포넌트] = useRef 처럼 submit 함수가 실행되면 ref가 부여된 input의 값을 한 번에 변경
복잡한 폼 만들 시 유용 (ex. Pw 입력 시 입력값이 ""로 표시되게 하는 기능)
[제어 컴포넌트] = 사용자 입력을 기반으로 state를 실시간으로 관리 (입력될 때마다 state 값이 실시간으로 변경 됨)
*단순한 폼 제작에 유용
따라서 기본적인 기능은 비제어 컴포넌트, 복잡한 것에만 제어 컴포넌트 활용하는 것이 BEST!!
yarn add react-hook-form
const ReactHookForm = ()=>{
// react-hook-form 에서 useForm을 제공합니다.
const {register , handleSubmit} = useForm()
// 등록하기 함수 -> handleSubmit이 조종해주는 함수 입니다.
const onClickSubmit = (data)=>{
console.log(data)
}
return(
<form onSubmit={handleSubmit(onClickSubmit)}>
<input type="text" {...register("writer")}/>
<input type="text" {...register("title")}/>
<input type="text" {...register("contents")}/>
<button type="reset"> 등록하기 </button>
</form>
)
}
export default ReactHookForm
useForm 에서 register, handleSubmit 을 꺼내와서 input에 입력되는 값을 받아올 수 있음
input 태그에 {...register("writer")] 를 넣어서 입력되는 값을 받아서 후에 data로 뽑아올 수 있는데 이 때 ("writer") 는 해당 data 객체의 key값이 됨
onClickSubmit 함수가 아닌 해당 함수를 인자로 받는 handleSubmit 함수에 받아짐
...register 기능으로 각각 key값을 부여받고, onSubmit으로 handleSubmit 함수에 전달되며, 이 전달된 data는 함수 내부에 formData에 객체 형태로 저장 됨onSubmit 함수 안의 인자로 해당 data 객체를 다시 집어넣어서 그대로 보냄onSubmit 함수는 data 매개변수에 해당 data객체를 인자로 받아 이를 조회해 볼 수 있게 됨+a) form 태그 내부의
button의 type
type = "reset" = form 내부의 input 값 모두 삭제
type = "submit" = form 내부의 input 값을 보냄(제출) - default 값
type = "button" = form 내부에 영향을 주지 않는 개별적ㅇ로 작동하는 기능을 만들고 싶을 때 사용
*form 내부의 button의 타입이 submit인 경우, 해당 버튼에 다른 함수를 바인딩하게 되면 예상치 못한 오류 발생 가능!!
[다른 기능을 주고 싶다면 꼭 type="button"을 주도록 할 것!!]
yup 이라는 라이브러리로 간단하게 대체 가능설치
yarn add @hookform/resolvers yup
react-hook-form 과 연결 및 사용
import * as yup from 'yup'
import {useForm} from 'react-hook-form'
import {yupResolver} from '@hookform/resolvers/yup'
// yup 에러메세지 생성해주기 -> 제어 컴포넌트 형태로 사용해야 합니다.
const schema = yup.object().shape({
myWriter : yup.string()
.email('이메일 형식이 적합하지 않습니다.')
.required('필수 입력값입니다.')
myPassword : yup.string()
.min(4,'비밀번호는 최소 4자리 이상입니다.')
.max(15,'비밀번호는 최대15자리 입니다.')
.required('필수 입력값 입니다.')
})
const ReactHookForm = ()=>{
//formState에서 에러메세지들을 받아오게 됩니다.
const {register , handleSubmit, formState} = useForm({
// schema는 위에서 만들어 둔 schema입니다.
resolver : yupResolver(schema),
mode : "onChange"
})
// 등록하기 함수 -> handleSubmit이 조종해주는 함수 입니다.
const onClickSubmit = (data)=>{
console.log(data)
}
return(
<form onSubmit={handleSubmit(onClickSubmit)}>
이메일 : <input type="text" {...register("myEmail")}/>
{ /* 우리가 생성한 yup의 에러메세지는 항상 errors에 담기는데 이 에러는 있을때도 있고 없을 때도 있기 때문에 옵셔널 체이닝을 붙여야 합니다. */}
<div> {formState.errors.myEmail?.message}</div>
비밀번호 : <input type="text" {...register("myPassword")}/>
<div> {formState.errors.myPassword?.message}</div>
<button styled={{ backgroundColor: formState.isValid ? "yellow" : "" }}> 등록하기 </button>
</form>
)
}
export default ReactHookForm
yup 같은 검증라이브러리는 보통 form 과 많이 사용하기 때문에 react-hook-form 과 연결해서 사용가능
useForm 안에 resolver로 yupResolver를 넣고 미리 설정해 둔 schema 구조도 넣어 줌
schema에는 form 에서 제출되는 각 input 입력값의 검증 조건들을 설정할 수 있음
-.string( ) = 문자열만 입력가능
-.email( ) = 이메일 형식에 맞는지 체크
-.required( ) = 반드시 값이 입력되어야 함
formState.errors.myEmail?.message 형식으로 요소를 만들어서 조건부렌더링에 따라 조건에 맞지 않다면 해당 에러메시지가 출력되도록 설정formState.isValid 로 지정한 모든 조건에 부합한다면 true를 반환하는 기능을 활용해 버튼 비활성화 여부 결정+a) useform - formstate의 각 기능들
<button type="button" onClick={onSave} disabled={!formState.dirty}>
Save
</button>
사용자가 form을 수정했다면 값은 true. (이를 활용해 값의 변경이 있을 때에만 버튼을 활성화하는 식으로 활용 가능)
{formState.isSubmitted && <span>Form submitted!</span>}
form이 제출되었는지 여부를 체크
<button type="submit" disabled={!formState.isValid}>Submit</button>
form이 유효한 지 (에러가 없는지) 여부를 체크
{formState.isValidating && <span>Validating...</span>}
form의 유효성 검사가 진행 중인 경우 이 값은 true. (이를 이용해 유효성 검사가 진행중인 동안 로딩바 등의 UI 요소 추가하여 표시 가능)
{formState.errors.email && <span>Please enter a valid email address</span>}
form 에 에러가 있을 경우 ```formState.errors.email.message``` 로 에러메시지 표시