🍇 useState
useState훅은 지역컴퍼넌트에서 데이터를 사용하기 위한 react hook이다
값이 변경될때마다, 리렌더링이 되는 특성이 있다.
예시
import { useState } from "react";
type UserType = {
name: string;
age: number;
};
export default function State() {
// 1. useState 사용할 객체 선언
const [user, setUser] = useState<UserType>({
name: "",
age: 0,
});
const onClickChangeName = () => {
// 객체 값 변경
setUser((prev) => {
return {
...prev,
name: "최아무개",
};
});
};
const onClickChangeAge = () => {
setUser({ ...user, age: 33 });
};
return (
<div className="container mx-auto">
<p className="mb-3 text-gray-500 dark:text-gray-400">
이름 : {user.name}
</p>
<p className="mb-3 text-gray-500 dark:text-gray-400">나이 : {user.age}</p>
<hr />
<button
onClick={onClickChangeName}
className="mt-2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
이름 변경
</button>
<button
onClick={onClickChangeAge}
className="mt-2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
나이 변경
</button>
</div>
);
}
- useState를 통해 데이터 타입을 선언한다(typescript이므로 객체의 타입을 name,age를 갖는 UserType을 선언하였다)
- const [user,setUser] = useState<UserType>({ name:"",age:0})
- 화면에 보여주기 위하여 user.name, user.age를 보여준다
- 객체의 값 변경을 위하여 button을 누르면 onClickChangeAge, onClickChangeName을 통하여 setUser함수를 호출하여 값을 변경한다
🚒 useEffect
useEffect는 React 컴퍼넌트가 렌더링이 된 후에 상태 또는 props가 바뀔때 발생하는 hook
보통은 컴퍼넌트의 렌더링 최초 발생 , 렌더링 소멸 할때와 특정 값을 감시할때 사용된다
렌더링 최초에 사용되는 경우
useEffect(() => {
// 최초 실행
console.log("최초 렌더링");
return () => {
// clean up
console.log("렌더링 소멸");
};
}, []);
첫번째 인자에 함수형태로 넣고, 두번째 인자에 빈배열을 넣는다.
그리고 소멸할때 처리 해야할것이 있다면 return 함수에서 처리한다
특정 값 감시
const [user, setUser] = useState<UserType>({
name: "",
age: 0,
});
useEffect(() => {
console.log("나이값 변경");
}, [user.age]);
const onClickChangeAge = () => {
setUser({ ...user, age: 33 });
};
useState를 통해 user를 선언하였고,
onClickChangeAge의 함수에서 setUser를 통해 값을 변경할 경우,
useEffect이 발동하면서, "나이값 변경"이라는 콘솔이 찍히게 된다.
🚜 useContext
useContext는 컴퍼넌트들 간의 전역적으로 데이터를 공유하기 위한 hook이다.
보통 컴퍼넌트들간의 props drilling 이 발생할때 사용된다
"use client";
import { createContext, useContext, useState } from "react";
type AuthContextType = {
isLogin: boolean;
login: (username: string) => void;
logout: () => void;
};
const AuthContext = createContext<AuthContextType | null>(null);
export default function ContextPage() {
const [islogin, setIsLogin] = useState(false);
const loginFunc = (username: string) => {
console.log(username);
setIsLogin(true);
};
const logoutFunc = () => {
console.log("logout");
setIsLogin(false);
};
return (
/** Provider를 통해 값을 공급 */
<AuthContext.Provider
value={{ isLogin: islogin, login: loginFunc, logout: logoutFunc }}
>
<Child />
</AuthContext.Provider>
);
}
function Child() {
/** 자식 컴퍼넌트에서 useContext를 통해 값을 사용 */
const contextValue = useContext(AuthContext);
return (
<>
<p>{contextValue?.isLogin ? "로그인됨" : "로그인 안됨"}</p>
<button onClick={() => contextValue?.login("test")}>로그인</button>
<button onClick={() => contextValue?.logout()}>로그아웃</button>
</>
);
}
AuthContext는 createContext를 통해 생성되었고, isLogin,login,logout가 필수로 필요하다
AuthContext.Provider를 통해 공급자 역할을 할 수 있다.
Child에서는 useContext를 통해, 전역으로 선언된 value를 사용할 수 있다.
value에는 함수도 전달이 가능하다
🚐 useCallBack
함수의 재생성을 방지하기 위해 사용하는 React Hook
"use client";
import { useCallback, useEffect, useState } from "react";
type ChildComponentProps = {
onClickBtn: () => void;
};
function ChildComponent({ onClickBtn }: ChildComponentProps) {
return (
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={onClickBtn}
>
버튼1
</button>
);
}
export default function CallBack() {
const [count, setCount] = useState(1);
console.log("CallBack rendering");
useEffect(() => {
console.log("count : ", count);
}, [count]);
// Count를 증가시킬때마다, onClickChildComponent가 새롭게 생성되지 않는다.
const onClickChildComponent = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<>
<div>
<ChildComponent onClickBtn={onClickChildComponent} />
</div>
</>
);
}
onClickChildComponent를 useCallback을 이용하지 않았다면, count가 증가할때마다 계속해서 onClickChildComponent가 새롭게 생성되는 것이 발생할것이다. onClickChildComponent를 사용하면 메모제이션때문에 이전값을 기억하여 새롭게 생성하지 않는다.
(다만 useCallback도 메모리를 사용하는것이기 때문에 속도가 느리지않다면 사용할 이유는 없다)
🚐 useMemo
useMemo는 계산된 값을 메모이제이션(기억)해서 불필요한 재계산을 방지하는 Hook입니다.
useCallback은 함수를 기억하는 것이라면, useMemo는 값을 기억하는 것이다
"use client";
import { useMemo, useState } from "react";
const cal = (num: number) => {
console.log("무거운 계산 중...");
for (let i = 0; i < 1e9; i++) {} // 일부러 느리게
return num * 2;
};
export default function MemoPage() {
const [num, setNum] = useState(0);
// 무거운 계산을 매번 렌더링할때마다, total을 리턴하는것보다는
// useMemo를 통해서 값이 변경될때마다 하는 것이 효율적이다
const total = useMemo(() => {
return cal(num);
}, [num]);
return (
<>
<p>num : {num}</p>
<p>total: {total}</p>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setNum((prev) => prev + 1)}
>
증가
</button>
</>
);
}
useMemo를 사용할 경우 total이 매번 렌더링될때마다 계산되는것이 아닌, num이 변경될때마다 계산되서 리턴하게 된다.
만약 화면이 렌더링이 잦은 경우에는 무거운 계산을 매번렌더링이 아닌, 값이 변경될때마다 하는것이 효율적이다.
🚜 useReducer
useReducer는 복잡한 상태 변경 로직을 한 곳에서 관리할 수 있게 해주는 Hook
"use client";
import { useReducer } from "react";
type State = {
name: string;
email: string;
password: string;
};
type Action =
| { type: "CHANGE_INPUT"; field: keyof State; value: string }
| { type: "RESET" };
const initialState: State = {
name: "",
email: "",
password: "",
};
// 1. reducer에서 state, action 인자 처리
function reducer(state: State, action: Action) {
// 2. action에 따라 state 상태 처리
switch (action.type) {
case "CHANGE_INPUT":
return { ...state, [action.field]: action.value };
case "RESET":
return initialState;
default:
return state;
}
}
export default function ReducerPage() {
// 3.useReducer를 통해 state,dispatch 추출
const [state, dispatch] = useReducer(reducer, initialState);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
// 4. 상태 변경에 따라 dispatch를 호출 하고 상태 변경
dispatch({
type: "CHANGE_INPUT",
field: e.target.name as keyof State,
value: e.target.value,
});
};
return (
<>
<form
onSubmit={handleSubmit}
className="flex flex-col gap-3 max-w-sm mx-auto p-4"
>
<h2 className="text-2xl font-semibold text-center mb-2">회원가입</h2>
<input
type="text"
name="name"
value={state.name}
onChange={handleChange}
placeholder="이름"
className="border p-2 rounded"
/>
<input
type="text"
name="email"
value={state.email}
onChange={handleChange}
placeholder="이메일"
className="border p-2 rounded"
/>
<input
name="password"
placeholder="비밀번호"
type="password"
value={state.password}
onChange={handleChange}
className="border p-2 rounded"
/>
<button
type="submit"
className="bg-blue-500 text-white rounded p-2 hover:bg-blue-600"
>
가입하기
</button>
</form>
</>
);
}
useReducer를 이용하면, 사용자의 입력 폼 같은 상황이 발생했을때,
이름,메일,비밀번호를 묶어서 관리 할 수 있다. key값에 대한 변경 reset 등등 한곳에서 관리가 용이하다
댓글