: 함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 그리고 렌더링 직후 작업을 설정하는 useEffect 등의 기능등을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해줍니다.
1. useState
: useState 는 가장 기본적인 Hook 으로서, 함수형 컴포넌트에서도 가변적인 상태를 지니고 있을 수 있게 해줍니다.
function Counter() {
const [value, setValue] = React.useState(0);
console.log(value);
return (
<div>
<p>
현재 카운터 값은 <b>{value}</b>
</p>
<button onClick={ ()=> { setValue(value + 1); console.log("plus btn : " + value); } }> +1 button</button>
<br></br>
<button onClick={ ()=> { setValue(value - 1); console.log("minus btn : " + value); } }> -1 button</button>
</div>
)
}
플러스 버튼 누를 시
plus btn : 0
1
다시 Counter가 호출 되면서 +1된 상태
2. useEffect
: useEffect 는 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정 할 수 있는 Hook 입니다. 클래스형 컴포넌트의 componentDidMount 와 componentDidUpdate 를 합친 형태로 보아도 무방합니다.
function Counter() {
const [value, setValue] = React.useState(0);
console.log(value);
React.useEffect(() => {
console.log('렌더링이 완료되었습니다! value : ' + value);
});
return (
<div>
<p>
현재 카운터 값은 <b>{value}</b>
</p>
<button onClick={ ()=> { setValue(value + 1); console.log("plus btn : " + value); } }> +1 button</button>
<br></br>
<button onClick={ ()=> { setValue(value - 1); console.log("minus btn : " + value); } }> -1 button</button>
</div>
)
}
plus btn : 0
1
렌더링이 완료되었습니다! value : 1
렌더린이 완료된 후 호출
만약 useEffect 에서 설정한 함수가 컴포넌트가 화면에 가장 처음 렌더링 될 때만 실행되고 업데이트 할 경우에는 실행 할 필요가 없는 경우엔 함수의 두번째 파라미터로 비어있는 배열
React.useEffect(() => {
console.log('렌더링이 완료되었습니다! value : ' + value);
}, []);
특정 값이 변경이 될 때만 호출
React.useEffect(() => {
console.log('렌더링이 완료되었습니다! value : ' + value);
}, [value]);
3. useContext
이 Hook 을 사용하면 함수형 컴포넌트에서 Context 를 보다 더 쉽게 사용 할 수 있습니다.
context api를 사용하면 전역으로 상태를 관리 할 수 있습니다.
userInfo.tsx
// 리액트와 함께 createContext를 import 해주기
import React, { ReactNode, createContext, useState } from "react";
import { UserInfo } from "../interface/interfaces";
import axios from "axios";
export const UserContext = createContext({});
// UserStore라는 이름으로 어떤 데이터를 만들지 생성
const UserStore = (props : {children : ReactNode}) => {
const [user, setUser] = useState<UserInfo>({
isLogin : false,
id: "",
name: "",
pw: "",
});
return (
// Provider를 사용해서 감싸주기
<UserContext.Provider value={{user, setUser}}>
{props.children}
</UserContext.Provider>
);
};
export default UserStore;
page1.tsx
const context : any = useContext(UserContext);
const user = context.user;
return(
<div>
<p>This is Page1 .... login userID : <b>{user.id}</b></p>
</div>
);
4. useMemo
useMemo는 React의 성능 최적화를 위한 Hook으로, 연산 비용이 큰 함수의 결과를 캐싱하여 불필요한 재계산을 방지하는 역할을 합니다.
- 렌더링 성능 최적화를 위해 사용
- 의존성이 변경될 때만 연산 수행
- 불필요한 재계산 방지
- 하지만 무조건 남발하면 오히려 성능 저하될 수 있음
import React, { useMemo } from 'react';
const MyComponent = ({ data }) => {
// useMemo를 사용하여 계산 결과를 메모이제이션
const expensiveCalculation = useMemo(() => {
// 여기서 data에 기반한 계산을 수행
// 예: 복잡한 계산 또는 데이터 필터링
return data.filter(item => item.someCondition).map(item => item.someValue);
}, [data]); // data가 변경될 때만 다시 계산
return (
<div>
{/* expensiveCalculation 값을 사용하는 UI */}
{expensiveCalculation.map((value, index) => (
<span key={index}>{value}</span>
))}
</div>
);
};
export default MyComponent;
5. useCallback
useCallback은 리액트에서 함수의 재생성을 방지하기 위해 사용하는 훅입니다. 이 훅은 특정 의존성이 변경되지 않으면, 동일한 함수 인스턴스를 재사용합니다. 주로 컴포넌트가 렌더링될 때마다 새로운 함수가 생성되는 것을 방지하기 위해 사용됩니다.
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// useCallback을 사용해 함수가 재생성되지 않도록 합니다.
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // 의존성 배열이 비어 있으므로, 컴포넌트가 처음 렌더링될 때만 생성됩니다.
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default Counter;
6. useReduce
const [<상태 객체>, <dispatch 함수>] = useReducer(<reducer 함수>, <초기 상태>, <초기 함수>)
redux가 뭔지 알았다면 이제 redux의 구조에 대해 알아보겠습니다.
redux에서 중요한 것은 상태를 직접 변경하는 것이 아닌 "action"을 통해 변경하는 것입니다. 즉, redux에서 중요한 것은 다음과 같습니다.
state
action
reducer
추가로 dispatch, selector는 예제를 진행하며 설명하겠습니다.
- state: 위의 내용과 동일, store에서 저장되어 있는 값.
- action: store에 저장된 값을 변경시키는 방식을 정함.
- reducer: (action, old state)를 받아서 new state로 변환시키는 함수.
const reducer = (state : any, action : string) => {
switch (action) {
case 'increment':
return state + 1
case 'decrement':
return state - 1
}
}
function Page3() {
const [count, dispatch] = React.useReducer(reducer, 0)
return (
<div>
Count: {count}
<button onClick={() => dispatch('increment')}>Increment</button>
<button onClick={() => dispatch('decrement')}>Decrement</button>
</div>
)
}
7. useImperativeHandle
: 부모 컴포넌트에서 자식 컴포넌트의 State를 관리해야할 경우 사용할 수 있는 훅입니다.
const iframeRef = useRef<any>(null);
//부모에서 호출...
useImperativeHandle(ref, () => {
return {
//iframe에 데이터 전달
postMesseageChild: () => {
if (iframeRef && iframeRef.current) {
console.log("[iframe < parents] postMesseageChild", labels);
const newData: any = {};
labels.map((item) => {
newData[item.labelId] = item.labelText1;
});
iframeRef.current.contentWindow.postMessage(
{
type: "label",
changeLabelList: newData
},
preview_domain,
);
}
},
};
}, [labels]);
<Button
imageUrl="/images/dot-right-arrow.png"
className="basic-btn mt-2 flex h-7 items-center justify-start"
onClick={() => {
if (previewRef && previewRef.current) {
previewRef.current.postMesseageChild();
}
}}>PREVIEW 적용
</Button>
8. useRef
useRef는 React의 훅(Hook) 중 하나로, 컴포넌트의 상태를 변경하지 않으면서 DOM 요소나 값을 저장하는 데 사용됩니다.
렌더링을 발생시키지 않고 값을 유지해야 할 때 유용합니다.
import { useRef } from "react";
function InputFocus() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus(); // input 요소에 포커스 설정
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Type here..." />
<button onClick={handleFocus}>포커스</button>
</div>
);
}
export default InputFocus;
- `useRef(null)`로 ref 객체 생성 → current 속성을 가짐
- `<input ref={inputRef} />`를 통해 input 요소와 연결
useRef로 값 유지 (렌더링 방지)
import { useRef, useState } from "react";
function Counter() {
const countRef = useRef(0); // 상태와 달리 값이 변경되어도 리렌더링되지 않음
const [renderCount, setRenderCount] = useState(0);
const increaseRef = () => {
countRef.current += 1;
console.log("Ref 값:", countRef.current);
};
const forceRender = () => setRenderCount(renderCount + 1);
return (
<div>
<p>Ref 값: {countRef.current}</p>
<button onClick={increaseRef}>Ref 값 증가</button>
<button onClick={forceRender}>리렌더링</button>
</div>
);
}
export default Counter;
- countRef.current 값이 변경되어도 컴포넌트는 리렌더링되지 않음 (state와 차이점)
- forceRender 버튼을 누르면 useState로 인해 리렌더링이 발생하여 countRef.current의 최신 값이 반영됨
useRef로 이전 상태 값 기억하기
import { useEffect, useRef, useState } from "react";
function PreviousValue() {
const [count, setCount] = useState(0);
const prevCountRef = useRef(count);
useEffect(() => {
prevCountRef.current = count;
}, [count]); // count 변경될 때마다 prevCountRef.current 업데이트
return (
<div>
<p>현재 값: {count}</p>
<p>이전 값: {prevCountRef.current}</p>
<button onClick={() => setCount(count + 1)}>+1 증가</button>
</div>
);
}
export default PreviousValue;
- prevCountRef.current를 사용해 이전 상태 값을 저장
- useEffect를 활용해 count가 변경될 때마다 prevCountRef.current를 업데이트
- 이전 값과 현재 값을 비교할 수 있음
document객체로 DOM접근 VS useRef
React와 연동 | ❌ React와 무관 (순수 DOM 조작) | ✅ React에서 권장 |
렌더링 영향 | ❌ React의 렌더링과 무관 | ✅ React 내부에서 관리 |
코드 가독성 | ❌ 비추천 (React 철학에 맞지 않음) | ✅ 더 깔끔하고 유지보수 쉬움 |
가상 DOM 동기화 | ❌ React가 변경 사항을 모름 | ✅ React가 인지할 수 있음 |
주로 사용하는 경우 | ✅ React 외부에서 DOM을 제어할 때 | ✅ React 컴포넌트 내부에서 DOM 접근이 필요할 때 |
9. useDeferredValue
useDeferredValue는 리액트에서 제공하는 훅으로, 값의 업데이트를 지연시켜 UI 성능을 개선할 때 사용됩니다. 주로 비동기 렌더링(Concurrent Rendering)을 활용하여, 사용자가 입력을 빠르게 할 때 렌더링 성능이 저하되지 않도록 도와줍니다.
import React, { useState, useDeferredValue } from 'react';
function Search() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<div>
<strong>Deferred Query: </strong>
{deferredQuery}
</div>
</div>
);
}
useDeferredValue는 성능을 최적화할 수 있지만, 사용에 신중해야 합니다. 너무 많은 상태를 지연시키면 오히려 불필요한 렌더링을 초래할 수 있기 때문에, 적절히 사용하는 것이 중요합니다.
https://velog.io/@velopert/react-hooks
https://velog.io/@jinyoung985/React-useMemo%EB%9E%80
https://velog.io/@qf9ar8nv/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C%EB%A5%BC-%ED%86%B5%ED%95%B4-Redux%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
'WEB개발 > REACT' 카테고리의 다른 글
[React] web성능 메트릭 (0) | 2023.12.18 |
---|---|
[React] Typescript (1) | 2023.12.18 |
[React] ServerComponent, HTML Streaming (0) | 2023.12.11 |
[Next.js] Tailwind CSS, ESLint, Webpack (0) | 2023.12.11 |
[React] Next.js (0) | 2023.12.05 |