8단원. HOOK
함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState
렌더링 직후 작업을 설정하는 useEffect

1. useState
- component 내에서 state를 관리시켜주며 초기값에서 사용
- component를 굳이 class형으로 바꿀 필요없음
숫자 카운터]
import React, { useState } from 'react';
const Info = () => {
const [name, setName] = useState('');
const [nickname, setNickname] = useState('');
const onChangeName = e => {
setName(e.target.value);
};
const onChangeNickname = e => {
setNickname(e.target.value);
};
return (
<div>
<div>
<input value={name} onChange={onChangeName} />
<input value={nickname} onChange={onChangeNickname} />
</div>
<div>
<div>
<b>이름:</b> {name}
</div>
<div>
<b>닉네임:</b> {nickname}
</div>
</div>
</div>
);
};
export default Info;
2. useEffect
- react component가 렌더링될때마다 특정 작업을 수행할 수 있도록 설정하는 HOOK
- 클래스형 컴포넌트의 componentDidMount와 componentDidUpdate를 합친 형태
- 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 useEffect에서 뒷정리**(cleanup) 함수를 반환**
import React, { useState, useEffect } from 'react';
const Info = () => {
const [name, setName] = useState('');
const [nickname, setNickname] = useState('');
useEffect(() => {
console.log('렌더링이 완료되었습니다!');
console.log({
name,
nickname
});
});
const onChangeName = e => {
setName(e.target.value);
};
const onChangeNickname = e => {
setNickname(e.target.value);
};
return (
(...)
);
};
export default Info;
3.useReducer
- 다양한 컴포넌트의 상황에 따라 state의 상태 관리를 구조적으로 할 수 있는 Hook
- 상태 변화 로직을 하나의 함수(리듀서)에서 관리할 수 있어서 복잡한 상태 로직을 처리할 때 유용
- useREducer에서 사용하는 액션 객체는 반드시 type을 지니고 있을 필요가 없음
(객체가 아니라 문자열이나 숫자도 상관없음)
const [state, dispatch] = useReducer(reducer, state객체)
function reducer(state, action) {
return {...}; // 불변성을 지키면서 업데이트한 새로운 상태를 반환합니다.
}
- reducer : dispatch에 의해 호출되어 state를 관리할 함수
- state객체 : 관리할 state
고정값]
reducer(state, action)
import React, { useReducer, useState } from "react";
import "./Todo.css";
const command = {
ADD: "ADD",
TOGGLE: "TOGGLE",
REMOVE: "REMOVE",
};
// TODO 상태를 위한 reducer 함수
const reducer = (state, action) => {
switch (action.type) {
case command.ADD:
return [...state, { id: Date.now(), text: action.text, compeleted: false }];
case command.TOGGLE:
return state.map((todo) =>
todo.id === action.id ? { ...todo, compeleted: !todo.compeleted } : todo
);
case command.REMOVE:
return state.filter((todo) => todo.id !== action.id);
default:
return state;
}
};
const Todo = () => {
const [todos, dispatch] = useReducer(reducer, []);
const [text, setText] = useState("");
return (
<div className="app">
<div className="todo-container">
<h2>📋 Todo List</h2>
<div className="input-container">
<input
value={text}
onChange={(e) => {
setText(e.target.value);
}}
/>
<button
onClick={() => {
dispatch({ type: command.ADD, text }); //text: 속성과 값이 똑같음
setText("");
}}
>
추가
</button>
</div>
<ul className="todo-list">
{todos.map((todo) => (
<li className="todo-item">
<div className={`todo-text ${todo.compeleted ? "completed" : ""}`}>{todo.text}</div>
<button onClick={() => dispatch({ type: command.TOGGLE, id: todo.id })}>완료</button>
<button onClick={() => dispatch({ type: command.REMOVE, id: todo.id })}>삭제</button>
</li>
))}
</ul>
</div>
</div>
);
};
export default Todo;
4. useMemo
- 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있음
- 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고,
원하는 값이 바뀌지 않았다면 이전에 연산했던 결과(기존의 값)를 다시 사용하는 방식
import React, { useState, useMemo } from 'react';
const getAverage = numbers => {
console.log('평균값 계산 중..');
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const onChange = e => {
setNumber(e.target.value);
};
const onInsert = () => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
};
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
};
export default Average;
5. useCallback
- callback 함수를 재사용
- 렌더링 성능을 최적화할때 사용
- useCallback( callback,[ ]) : 컴포넌트가 처음 렌더링될 때만 함수를 생성
- useCallback( callback,[state]) :지정한 state가 변경될 때만 함수를 생성
- array.reduce(callback, initialValue)
배열의 총합, 평균, 합치기에 사용되는 배열 메서드
1. callback(accumulator, current, currentIndex, array)
2. accumulator : reduce를 한 이전 결과, 처음인 경우 initialValue가 있으면 initialValue
3. current : 처리할 현재 요소
4. currentIndex : 처리할 현재 요소의 index, initialValue가 있으면 0, 없으면 1부터 시작
5. array : reduce를 시작한 배열
callback(accumulator, current, currentIndex, array)
accumulator : reduce를 한 이전 결과, 처음인 경우 initialValue가 있으면 initialValue
current : 처리할 현재 요소
currentIndex : 처리할 현재 요소의 index, initialValue가 있으면 0, 없으면 1부터 시작
array : reduce를 시작한 배열
render 함수에서 호출되고 있기 때문에 list가 변하지 않았는데도
input 양식의 값이 변경되면 (값을 입력, 수정만 해도) number state가 변경되므로
render 함수가 호출된다!
==> 같이 getAverage 함수도 호출된다.
--> useMemo를 이용해서 변경됐을때만 호출되게 수정하기
import React, { useCallback, useMemo, useRef, useState } from "react";
const getAverage = (numbers) => {
if (numbers.length === 0) return 0;
if (numbers.length === 1) return numbers[0];
const sum = numbers.reduce((a, b) => {
console.log("a:", a, "b:", b);
return a + b;
});
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const avg = useMemo(() => getAverage(list), [list]);
const prevOnChange = useRef();
const prevOnClick = useRef();
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []);
const onClick = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
}, [list, number]);
console.log(" onChange :", prevOnChange.current === onChange);
console.log(" onClick :", prevOnClick.current === onClick);
prevOnChange.current = onChange;
prevOnClick.current = onClick;
return (
<div>
<input value={number} onChange={onChange} />
<button onClick={onClick}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>평균값 : {avg}</div>
</div>
);
};
export default Average;
6. useRef
함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해줌
- useRef를 사용하여 ref를 설정하면 useRef를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가리킴
import React, { useState, useMemo, useCallback, useRef } from 'react';
const getAverage = numbers => {
console.log('평균값 계산 중..');
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputEl = useRef(null);
const onChange = useCallback(e => {
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputEl.current.focus();
}, [number, list]); // number 혹은 list가 바뀌었을 때만 함수 생성
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} ref={inputEl} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
};
export default Average;
로컬 변수 사용하기]
렌더링과 상관없이 값이 바뀔 수 있음
import React, { useRef } from 'react'; //함수형
const RefSample = () => {
const id = useRef(1);
const setId = (n) => {
id.current = n;
}
const printId = () => {
console.log(id.current);
}
return (
<div>
refsample
</div>
);
};
export default RefSample;
7. 커스텀 HOOK 만들기
import React from 'react';
import useInputs from './useInputs';
const Info = () => {
const [state, onChange] = useInputs({
name: '',
nickname: ''
});
const { name, nickname } = state;
return (
<div>
<div>
<input name="name" value={name} onChange={onChange} />
<input name="nickname" value={nickname} onChange={onChange} />
</div>
<div>
<div>
<b>이름:</b> {name}
</div>
<div>
<b>닉네임: </b>
{nickname}
</div>
</div>
</div>
);
};
export default Info;
8. HOOK 정리
HOOKs 패턴을 사용하면 클래스형 컴포넌트를 작성하지 않고도 대부분의 기능 구현 가능
따라서 앞으로의 컴포넌트의 경우 함수형 컴포넌트 + HOOKs 사용 권장
'FrontEnd > React' 카테고리의 다른 글
| [ React ] TODO LIST 만들기 (3) | 2025.04.09 |
|---|---|
| [ React ] 컴포넌트 스타일링 (0) | 2025.04.08 |
| [ React ] Component Recycle Method (0) | 2025.04.06 |
| [ React ] Element handling & Dom (0) | 2025.04.03 |
| [ React ] JSX Component & Props & State (1) | 2025.04.02 |