class를 사용할 때는
React.createRef
!!! 함수형에서는React.useRef
React 컴포넌트 렌더링 프로세스에 영항을 주지 않으면서 다음 렌더링에도 값을 기억하기 위한 수단
웹 표준 animation
useEffect 콜백보다 먼저 실행된다.
> pnpm add gsap
id가 중첩되는 컴포넌트를 생성하면 이벤트 발생시 동작하지 않는 에러가 발생한다. 즉, class, id, 요소를 하나만 선택하는 보장을 할 수 없다.
id, class 대신 useRef를 사용하자.
1. Refs 생성
2. JSX 요소 ref 속성(prop)에 Refs 연결
3. useLayoutEffect 훅 안에서 Refs 현재(current) 값으로 명령형 프로그래밍
React 컴포넌트는 순수해야한다. (렌더링 프로세스 순수해야 하기 때문)
Web Animation, GSAP, jQuery와 같은 API는 명령형 프로그래밍이다.
그러므로 컴포넌트 내부에 직접 사용할 수 없다.
명령형 프로그래밍을 작성힐 수 있는 구간은 두군데 이다.
1. Effect Hook (useLayoutEffect Hook 브라우저 페인팅 이전, 성능 이슈가 발생할 수 있기 때문에 이벤트 핸들링 할 때만 사용하자)
2. Event Handler(Listener)
React 요소(가상)가 실제 DOM에 렌덜이 된 후 요소를 참조하려면 ?
기존의 document.getElementById, document,querySelector 사용하는 것이 권장되지 않는다.
따라서 useRef Hook을 사용해 Refs 객체를 생성하여 활용
const anyRef = useRef();
과거 코드
gsap.context()는 React 개발자에게 범위 내에서 CSS 선택자를 사용하는 옵션과 애니메이션 정리(cleanup) 기능을 제공
사용하는 이유?
React 전용! 선언형!
> pnpm add framer-motion
import { motion } from 'framer-motion';
import { useState } from 'react';
function FramerMotion_Animation() {
const [count, setCount] = useState(0);
const handleCountUp = () => {
setCount((c) => c + 10);
};
const [key1, setKey1] = useState(Math.random());
const [key2, setKey2] = useState(Math.random());
const [key3, setKey3] = useState(Math.random());
const [keys, setKeys] = useState(
Array(3)
.fill(null)
.map(() => Math.random())
);
const handleResetAnimation = () => {
setKey1(Math.random());
setKey2(Math.random());
setKey3(Math.random());
setKeys(keys.map(() => Math.random()));
};
return (
<>
<h2>컴포넌트 내부의 DOM 요소를 직접 참조하는 Refs</h2>
<div className="flex gap-2">
<button
type="button"
className="my-6 py-1.5 px-2.5 border border-slate-200 rounded-md shadow-lg"
onClick={handleCountUp}
>
{count}
</button>
<button
type="button"
className="my-6 py-1.5 px-2.5 border border-slate-200 rounded-md shadow-lg"
onClick={handleResetAnimation}
>
<svg
className="w-4 h-4 text-gray-800 dark:text-white"
aria-hidden="true"
fill="none"
viewBox="0 0 18 20"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1}
d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"
/>
</svg>
</button>
</div>
<div className="flex flex-col gap-[700px]">
<Circle key={key1} />
<Circle key={key2} />
<Circle key={key3} />
{keys.map((key) => (
<Circle key={key} />
))}
</div>
</>
);
}
function Circle() {
return (
<motion.figure
role="none"
className="grid place-content-center w-16 h-16 rounded-full bg-yellow-300"
initial={{ y: -150, opacity: 0 }}
whileInView={{
y: 0,
opacity: 1,
scale: 6,
}}
viewport={{
once: true,
}}
transition={{
duration: 4,
type: 'spring',
stiffness: 400,
damping: 5,
}}
>
<motion.img
// 초기 상태(initial)
initial={{ scale: 0 }}
// 애니메이션 상태(animate)
animate={{ rotate: 180, scale: 1 }}
// 트랜지션 상태(transition)
transition={{
type: 'spring',
stiffness: 260, // 뻣뻣함 (팽팽함)
damping: 20, // 마찰(제동)
}}
src="/react.png"
alt="React"
className="w-10 h-10"
/>
</motion.figure>
);
}
export default FramerMotion_Animation;
PB는 사용자 브라우저 로컬 스토리지에 저장한다.
clear메서드를 사용해 데이터를 삭제한다.
인증 컨텍스트 생성
src/context/Auth.jsx
// 컨텍스트
// 1. Create Context (React.creactContext)
// 3. 컨텍스트 프로바이더를 앱을 감쌈
// 2. Context Provider를 사용해 값value을 공급provide
// 컴포넌트
// 1. useContext Hook을 사용해서 공급된 context value를 주입(injection)
// 2. JSX 또는 이벤트 핸들러 내부에서 값을 사용
/* ----------------------------------------------------------------------- */
import pb from '@/api/pocketbase';
import { createContext, useState, useEffect } from 'react';
// Context 생성
const Authcontext = createContext();
// 초기 인증 상태
const initialAuthState = {
isSignin: false,
user: null,
token: '',
};
// Context.Provider 래퍼 컴포넌트 작성
function AuthProvider({ displayName = 'AuthProvider', children }) {
// 인증상태 pb.authStore = { isValid, model, token } 객체로 묶어서 관리
const [authState, setAuthState] = useState(initialAuthState);
useEffect(() => {
const unsub = pb.authStore.onChange((token, model) => {
console.log(token);
console.log(model);
setAuthState((state) => ({
...state,
isAuth: !!model,
user: model,
token,
}));
});
return () => {
unsub?.();
};
}, []);
// 메서드: 할 수 있는 기능
// 회원가입, 로그인, 로그아웃, 탈퇴
// 서버는 대기 시간(비동기/ 요청/응답)
const signUp = async (registerUser) => {
return await pb.collection('users').create(registerUser);
};
const signIn = async (usernameOrEmail, password) => {
return await pb
.collection('users')
.authWithPassword(usernameOrEmail, password);
};
const signOut = async () => {
return await pb.collection('users').collectionIdOrName();
};
const secession = async (recordId) => {
// localstorage에서 데이터를 지우기 때문에 asyc/await가 필요 없긴하다.
return await pb.collection('users').delete(recordId);
};
const authValue = {
...authState,
signUp,
signIn,
signOut,
secession,
};
return (
<Authcontext.Provider value={authValue} displayName={displayName}>
{children}
</Authcontext.Provider>
);
}
export default AuthProvider;
하위 컴포넌트로 Auth 정보를 공급할 수 있다.
검증되지 않은 prop을 검사해보자
> pnpm add prop-types
인증(authentication)된 사용자만 이용 가능한 페이지
- typescript
propTypes
pnpm add react-hot-toast