Hooks 사용 규칙
Hooks 종류
useState()
: 상태 관리를 위한 가장 기본적인 훅
const [count, setCount] = useState(0);
useRef()
: 참조(reference)를 생성하고 관리할 수 있는 훅 (DOM 접근, 변수 보존 등)
useEffect()
: 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 훅
useEffect(콜백함수, 의존성 배열)
의 형태이다. 배열에 뭘 넣느냐에 따라 앞의 함수가 언제 실행 될 지 다르게 동작된다. 넣어도 되고 안넣어도 된다.useMemo()
: 메모이제이션을 통해 함수의 리턴 값을 재사용할 수 있게 해주는 훅 (메모지에이션 된 값을 반환)
useCallback()
: 함수를 메모이제이션하여 불필요한 렌더링을 줄이게 해주는 훅 (메모지에이션 된 함수를 반환)
useReducer()
: 복잡한 컴포넌트 상태 로직을 리듀서 함수를 통해 관리할 수 있는 훅
useContext()
: 리액트에서 전역적으로 접근 가능한 데이터나 함수를 관리하고, 필요한 컴포넌트에서 그 값을 바로 가져와 사용할 수 있게 도와주는 훅
useMemo()
const functionName= useMemo(callback, [dependency])
[dependency]
가 바뀔 때 callback
실행.callback
의 값을 return한다.import { useMemo, useState } from "react";
export default function UseMemoEx() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
//불필요한 연산을 방지, 값을 기억을 한다.
//calc 함수 자체는 [count]의 변경이 있을 때 만 다시 연산하여 calc에 담아둔다.
//[count]는 useState로 배열로 반환되기 때문에 배열 형태로 넣어주면 된다.
const calc = useMemo(() => { console.log("calc.....✅");
return count * 2 }, [count]);
return (
<>
<div>
const: {count}
<button onClick={() => setCount(count + 1)}> + 1 </button>
</div>
<div>위에서 선언한 {calc} 자체가 들어가게 된다.[count]의 변경이 있을 때 만! </div>
<div>calc : {calc} </div>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
</>
);
}
useCallback()
useMemo()
에서는 값을 최적화하지만 다시 Redering 될 때 함수를 다시 불러오는 것을 막는다. (특정 값을 기억하여 불필요한 연산을 방지)useCallback()
에서는 메모지에이션 된 함수를 반환한다! (특정 함수를 기억하여, 불필요한 재선언을 방지)useMemo()
는 특정 결과값을 재 사용할 때 사용, useCallback()
은 특정 함수를 새로 만들지 않고 재 사용하고 싶을 때 사용한다고 생각하면 된다. 즉 함수를 기억해 주는 것!const memoizedCallback = useCallback(function, [dependency]);
의 형태이다.[dependency]
가 바뀌기 전까지 기존 함수를 재사용 하는것.const add = () => x + y;
//useCallback을 적용하면 아래처럼 된다.
const add = useCallback(() => x + y, [x, y]);
//--------------------예시 1
import { useCallback, useState } from "react";
export default function UseCallbackEx() {
const [text, setText] = useState("");
//의존성 배열이 빈 값인 경우, 처음 마운트 될 때 선언된 함수를 계속 기억하고 있는다.
//컴포넌트가 update 될 때 다시 선언하지 않고 기억하고 있는 함수를 사용한다.
//컴포넌트 내부에서 변경될 수 있는 값은 대표적으로 state, props가 있다.
//아래 handleOnChange 함수에서는 UseCallbackEx컴포넌트에서 유일하게 변경될 수 있는 값인 text를 활용하고 있지 않다.
//그러니 함수도 굳이 계속 선언 될 필요가 없게 된다.
const handleOnChange = useCallback((e) => {
setText(e.target.value);
}, []);
return (
<>
<h3>UseCallback 공부</h3>
<input type="text" value={text} onChange={handleOnChange} />
</>
);
}
//--------------------예시 2
//--------------------app.js내용
//const [postId, setPostId] = useState(1);
//<UseCallbackEx2 postId={postId} />
//<button onClick={() => setPostId(postId + 1)}> +1 </button>
//------------------------------------------------------------
import { useCallback, useEffect, useState } from "react";
export default function UseCallbackEx2({ postId }) {
const [post, setPost] = useState();
const fetchData = useCallback(async () => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
//위에서 불러온 응답을 json형태로 변환
//버튼 클릭마다 postId + 1 되게 되고
//[postId]가 바뀔 때 마다 json데이터도 결국 바뀌게 되는 것.
const post = await res.json();
setPost(post);
}, [postId]);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<>
<h3>useCallback 공부 2</h3>
<div>조회한 포스트 ID: {postId}</div>
{post && (
<div>
<div>id: {post.id}</div>
<div>title: {post.title}</div>
<div>body: {post.body}</div>
</div>
)}
</>
);
}
useReducer()
useState()
기반이다.useState()
, 복잡한 상태라면 useReducer()
로 사용하면 편리하다. (도움 받은 링크)- 컴포넌트의 업데이트 로직을 컴포넌트 외부로 뺄 수 있다.
- `useState()`의 대체 함수로, 다양한 컴포넌트 상황에 따라 상태값을 설정할 수 있다.
const [state, dispatch] = useReducer(reducer, initialValue);
의 형태이다.- `state` : 현재 상태
- `dispatch` : 액션을 발생시키는 함수. `reducer`를 실행시킨다.
- `reducer` : `state` 를 업데이트하는 함수. (**실질적으로 상태를 업데이트 하는 함수**이다. 액션을 발생 시키는 함수는 `dispatch` 이다. 결국 `**dispatch` 가 실행시키는 함수가 `reducer`** 인 것!)
- `initialValue` : initialState 상태의 초기값
import React, {useReducer} from 'react';
import './style.css';
function reducer(state, action){
// state값과 action의 type을 통해 reducer가 실행되기 때문에
//현재 state 객체의 count에서 1을 빼고, 더하고의 값을 반환
switch(action.type){
case "decrement":
return {count: state.count - 1};
case "increment":
return {count: state.count + 1};
}
}
export default function App() {
// useState기반이기 때문에 비슷한 형태.
//[number, setNumber] 에서 결국 뒤의 함수가 세터 함수이고 상태 변경을 해준다.
//그러니 뒤에 액션을 발생시키는 dispatch함수를를 작성해주면된다.
//그리고 useReducer로 상태를 관리하는거기 때문에 내부에 초기값 작성.
const [number, dispatch] = useReducer(reducer, {count:0});
return (
<>
{/* state의 number에 있는 count를 불러와야 하기 때문에 {number.count} */}
<h1>Count: {number.count}</h1>
{/* 클릭 시 dispatch함수를 실행한다. 지정된 type의. */}
<button onClick={()=> dispatch({type:"decrement"})}> 빼기 </button>
<button onClick={()=> dispatch({type:"increment"})}> 더하하기 </button>
</>
);
}
import { useReducer, useState, useCallback } from "react";
const initialValue = { value: 0 };
const reducer = (prevState, action) => {
//---(3) 현재 state와 action값을 받아 새로운 state를 반환한다.
//reducer는 매개변수 두개를 받는다. 1. 이전 state 2. 어떤 액션 할 지에 대한 정보
//action값을 따라 state값을 변경해준다. 조건을 걸어서!
switch (action.type) {
case "PLUS":
return { value: prevState.value + 1 };
case "MINUS":
return { value: prevState.value - 1 };
case "RESET":
return initialValue;
case "MULTIFLY":
return { value: prevState.value * action.number };
case "DIVIDE":
return { value: prevState.value / action.number };
default:
return { value: prevState.value };
}
};
export default function UseReducer() {
//---(1) reducer를 정의한다.
//useReducer()내부에 있는 reducer, initialValue는 컴포넌트 외부로 뺀다. (상단에 선언)
//dispatch를 통해 reducer가 실행된다. (액션을 발생시키는 함수)
const [state, dispatch] = useReducer(reducer, initialValue);
const [number, setNumber] = useState(0);
const handleChangeNumber = useCallback((e) => setNumber(e.target.value), []);
//---(2) dispatch를 통해 action값을 전달한다.
//dispatch는 action값을 받아서 state와 함께 reducer에 전달한다.
const plus = () => dispatch({ type: "PLUS" });
const minus = () => dispatch({ type: "MINUS" });
const reset = () => dispatch({ type: "RESET" });
const multifly = () => dispatch({ type: "MULTIFLY", number: number });
const divide = () => dispatch({ type: "DIVIDE", number: number });
return (
<>
<div>
{state.value}
<button onClick={plus}>+1</button>
<button onClick={minus}>-1</button>
<button onClick={reset}>reset</button>
<br />
<input type="number" value={number} onChange={handleChangeNumber} />
<button onClick={multifly}>곱하기</button>
<button onClick={divide}>나누기</button>
</div>
</>
);
}
useState()
도 있잖아.//만약에 곱하기 나누기 등 더 많은 연산을 이용한다고 하면? 또 컴포넌트 자체가 복잡해져서 코드가 길어진다면?
//여기저기서 setState가 실행되고 호출된다 복잡해진다.
//state의 변화를 추적하고 싶다면. 어떤 상황에 어떻게 state가 변하는가에 대해. setState를 일일이 찾아가면서 +1, -1이 되는구나~ 순차적으로 알 수 밖에 없다.
//reducer를 사용한다면?
//이 때 코드 효율성을 위해 사용한다. 더 찾기 편해진다. reducer만 보면 되니까.
//결국 연산작업 자체를 한 곳에 몰아두는 것으로 생각하면 된다.
const [state, setState] = useState(initialValue);
const plus = ()=> setState({value : state.value + 1})
const minus = ()=> setState({value : state.value - 1})
const reset = ()=> setState(initialValue)
}
useState()
를 사용useReducer()
를 사용 (객체, 배열 같이 하위 요소가 많은 경우)CustomHook
use
로 시작하는 파일을 만드는 것 이 관례이다.//--------------------customHook을 사용하는 파일
import { useState } from "react";
import useToggle from "../hooks/useToggle";
export default function CustomHookEx() {
const [isPopup, togglePopup] = useToggle(false);
return (
<>
<h3>CustomHook 공부</h3>
{isPopup && <div>보인다</div>}
<button onClick={togglePopup}>토글토글</button>
</>
);
}
//--------------------hooks 폴더 안에 작성한 커스텀훅 파일
import { useState } from "react";
//자주 쓰이는 toggle기능. true 이면 false로, false면 true로 변환시키는 상황.
//이에 따라 처리를 할 일이 자주 생긴다 라고 가정하고, 이 로직 자체를 훅으로 만드는 것!
export default function useToggle(initialValue) {
const [value, setValue] = useState(initialValue);
const toggle = () => {
setValue(!value);
};
return [value, toggle];
}
useCallback()
useCallback
사용![dependency]
가 바뀌기 전까지 기존 함수를 재사용useMemo()
callback
함수의 값을 return한다.useRef()
useContext()
: