-
TIL-2024.04.30 - React - 006. Hooks> Frontend/React 2024. 5. 1. 21:07
질문
1. Class Component 에서는 Life Cycle Methods를 사용해서 수명주기 동안 다양한 작업을 수행할 수 있는데, Functional Component에서는 어떻게 할 수 있어?
2. 각각 종류를 알려줘.
Hooks
- 함수형 컴포넌트에서 상태(state)와 다른 React 기능들을 사용 가능.
- 클래스 컴포넌트에서만 사용할 수 있던 기능들을 함수형 컴포넌트에서도 사용할 수 있음.
Hooks 종류
- useState
- useEffect
- useReducer
- useMemo
- useCallback
- useRef
- useContext (Context API시 논의)
- 커스텀 Hooks
useState
- 함수형 컴포넌트에서 상태를 추가
- useState를 사용하면 함수형 컴포넌트에서도 상태를 관리
예제:
import React, {useState} from 'react'; const Info = () => { // useState: 변수명, 설정 함수 , 초기 값 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 type="text" value={name} onChange={onChangeName} placeholder="이름"/> <input type="text" value={nickname} onChange={onChangeNickname} placeholder="닉네임"/> </div> <div> <div> <b> 이름 : </b> {name} </div> <div> <b> 닉네임 : </b> {nickname} </div> </div> </div> ) }; export default Info;
useEffect
- 함수형 컴포넌트에서 부수 효과(side effects)를 수행
- 예를 들어 데이터를 가져오거나 구독을 설정하는 등의 작업을 수행.
예제:
import React, {useState, useEffect} from 'react'; const Info = () => { // useState: 변수명, 설정 함수 , 초기 값 const [name, setName] = useState(''); const [nickname, setNickname] = useState(''); // useEffect는 기본적으로 랜더링되고 난 직후마다 실행되며, 두 번째 파라미터 배열에 무엇을 넣는지에 따라 실행되는 조건이 다름. // 1. 랜더링 될때 (생성 및 업데이트)될때마다 실행 > 값이 변경되면 재실행 useEffect(() => { console.log('기본 useEffect', name, nickname); }); // 2. 빈 [] > 마운트 될때만 실행 값이 변경되어도 재실행 X > useEffect(() => { console.log('[] 사용한 useEffect', name, nickname); }, []); // 3. []안 명시 > 특정 값이 업데이트될때만 실행 useEffect(() => { console.log('특정 값이 업데이트 될때만 실행되는 useEffect', name, nickname); }, [name]); // 4. cleanUp -1 > 빈 배열 사용한 경우 : unMounted 될 경우 호출 useEffect(() => { // 컴포넌트가 unMounted / update 되기 직전에 어떠한 작업을 수행하고 싶을 경우 cleanUp 함수 반환 return () => { console.log("cleanUp 함수 사용하여, unMounted 되기 전에 실행:: "); }; }, []); // 5. cleanUp -2 > [] 안 특정 값 사용: unMounted 될 경우 호출 useEffect(() => { // 컴포넌트가 unMounted / update 되기 직전에 어떠한 작업을 수행하고 싶을 경우 cleanUp 함수 반환 return () => { console.log("cleanUp 함수 사용하여, name 이 update 되기 전에 실행::", name); }; }, [name]); const onChangeName = e => setName(e.target.value); const onChangeNickname = e => setNickname(e.target.value); return ( <div> <div> <input type="text" value={name} onChange={onChangeName} placeholder="이름"/> <input type="text" value={nickname} onChange={onChangeNickname} placeholder="닉네임"/> </div> <div> <div> <b> 이름 : </b> {name} </div> <div> <b> 닉네임 : </b> {nickname} </div> </div> </div> ); }; export default Info;
useReducer
- 함수형 컴포넌트에서 Redux와 비슷한 방식으로 상태를 관리.
- useReducer를 사용하여 복잡한 상태 로직을 더 쉽게 관리.
예제
import React, {useReducer} from "react"; /* * useReducer 는 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해 주고 싶을 때 사용. * reducer 는 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 액션 값을 전달받아 새로운 상태로 반환하는 함수. * reducer 에서 새로운 상태를 만들 때는 반드신 불변성을 지켜야 함. */ const reducer = (state, action) => { // action.type 에 따라 다른 작업 수행 switch (action.type) { case 'INCREMENT': return {value: state.value + 1}; case 'DECREMENT': return {value: state.value - 1}; default: return state; } }; const Counter = () => { /** * useReducer(reducer 함수, 해당 리듀서의 기본값)을 parameter를 받아서, state & dispatch 함수를 받아옴. * State > 현재 가리키고 있는 상태 * Dispatch > Action 을 발생시키는 함수 * */ const [state, dispatch] = useReducer(reducer, {value: 0}); return ( <div> <p> 현재 카운터 값은 {state.value} 입니다</p> <button onClick={() => dispatch({type: 'INCREMENT'})}>+1</button> <button onClick={() => dispatch({type: 'DECREMENT'})}>-1</button> </div> ) }; export default Counter;
useMemo
- useMemo 훅은 계산 비용이 많은 연산의 결과를 기억하고, 의존성이 변경될 때만 재계산
- 주로 계산 비용이 많은 연산을 최적화하는 데 사용.
예제
import React, {useMemo, useState} from "react"; /* * useMemo > 내부에서 발생하는 연산을 최적화 * useEffect 와 비슷하게, [] 안의 값을 줘서, 해당 값이 변경될 때만 수행 */ const getAverage = (numbers) => { 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 onClick = () => { const newList = [...list, parseInt(number)]; setList(newList); setNumber(''); }; const listArr = list.map((item, index) => { return (<li key={index}>{item}</li>); }); const avg = useMemo(() => getAverage(list), [list]); // []안의 내용이 바뀔 때만, getAverage 함수 수행. 여기서는 list 변할 때만, getAverage() 호출 return ( <div> <input type="number" value={number} onChange={onChange}/> <button onClick={onClick}>등록</button> <ul>{listArr}</ul> <div> {/*<b>평균값: </b> {getAverage(list)}*/} {/*이 상태로 사용되면, 인풋이 수정될 때마다 호출됨으로 메모리 낭비*/} {/* useMemo를 사용하여 최적화 작업 수행*/} <b>평균값: </b> {avg} </div> </div> ); }; export default Average;
useCallback
- useCallback 훅은 콜백 함수를 메모이제이션하여 의존성이 변경될 때만 새로운 콜백 함수를 생성.
- 주로 자식 컴포넌트에 콜백 함수를 전달할 때 사용되며, 불필요한 렌더링을 방지하여 성능을 최적화
예제
import React, {useMemo, useCallback, useState} from "react"; /* * useCallback > 랜더링 성능 최적화를 위해 사용되며, 컴포넌트가 리렌더링될 때마다, 새로 만들어진 함수를 사용해주는데 이를 막기 위해 사용되는 메모이제이션 기술 (함수의 반환 값을 캐시하여 동일한 인자가 입력될 때 이전에 계산된 값을 반환하는 기술) * 1번째 parameter > 생성하고 싶은 함수 * 2번째 parameter > 배열을 넣어, 어떤 값이 바뀌었을 때 함수를 새로 생성하는지 명시 * 빈 배열 [] 인 경우, 컴포넌트가 랜더링될 때 만들었던 함수를 계속 사용 (매 렌더링마다 새로 생성) * 배열안에 값을 넣을 경우 [number, list] 인 경우, number 혹은 list가 바뀔 때 새로 만들어진 함수를 사용 */ const getAverage = (numbers) => { 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 = useCallback(e => { setNumber(e.target.value); }, []); // 차 있는 배열을 사용하는 경우, number | list 가 변경될 때만, 새로 생성됨 const onClick = useCallback(() => { const newList = [...list, parseInt(number)]; setList(newList); setNumber(''); }, [number, list]); // number | list 가 변했을 때만 수행 const listArr = list.map((item, index) => { return (<li key={index}>{item}</li>); }); const avg = useMemo(() => getAverage(list), [list]); // []안의 내용이 바뀔 때만, getAverage 함수 수행. 여기서는 list 변할 때만, getAverage() 호출 return ( <div> <input type="number" value={number} onChange={onChange}/> <button onClick={onClick}>등록</button> <ul>{listArr}</ul> <div> {/*<b>평균값: </b> {getAverage(list)}*/} {/*이 상태로 사용되면, 인풋이 수정될 때마다 호출됨으로 메모리 낭비*/} {/* useMemo를 사용하여 최적화 작업 수행*/} <b>평균값: </b> {avg} </div> </div> ); }; export default Average;
useRef
- useRef 훅은 컴포넌트의 렌더링과 상관없이 변수를 유지하고자 할 때 사용
- 주로 DOM 요소의 참조를 보관하거나, 이전 값을 유지하는 데 사용
예제
import React, {useMemo, useState, useCallback, useRef} from "react"; /* * useRef > 함수형 컴포넌트에서 ref를 더 쉽게 사용할 수 있도록 함. * ref 안의 값이 바뀌어도, 컴포넌트가 렌더링되지 않음. * 목표: 등록 버튼을 클릭한 경우, 포커스가 인풋쪽으로 넘어가도록 함 */ const getAverage = (numbers) => { 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); }, []); // 차 있는 배열을 사용하는 경우, number | list 가 변경될 때만, 새로 생성됨 const onClick = useCallback(() => { const newList = [...list, parseInt(number)]; setList(newList); setNumber(''); inputEl.current.focus(); // 객체 안의 current 값이 실제 element를 가리켜, 포커스가 인풋쪽으로 넘어감. }, [number, list]); // number | list 가 변했을 때만 수행 // []안의 내용이 바뀔 때만, getAverage 함수 수행. 여기서는 list 변할 때만, getAverage() 호출 const avg = useMemo(() => getAverage(list), [list]); const listArr = list.map((item, index) => { return (<li key={index}>{item}</li>); }); return ( <div> <input type="number" value={number} onChange={onChange} ref={inputEl}/> <button onClick={onClick}>등록</button> <ul>{listArr}</ul> <div> {/*<b>평균값: </b> {getAverage(list)}*/} {/*이 상태로 사용되면, 인풋이 수정될 때마다 호출됨으로 메모리 낭비*/} {/* useMemo를 사용하여 최적화 작업 수행*/} <b>평균값: </b> {avg} </div> </div> ); }; export default Average;
custom Hooks (using useReducer)
예제
import React, {useReducer} from "react"; /* * customHooks > 스스로 만든 Hooks * 목표: 여러 개의 인풋을 관리하기 위해 useReducer로 작성했던 로직을 useInputs라는 Hook으로 분리 */ const reducer = (state, action) => { return { ...state, [action.name]: action.value }; }; const useInputs = (initialForms) => { const [state, dispatch] = useReducer(reducer, initialForms); const onChange = e => dispatch(e.target); return [state, onChange]; }; export default useInputs;
답변
1. Class Component 에서는 Life Cycle Methods를 사용해서 수명주기 동안 다양한 작업을 수행할 수 있는데, Functional Component에서는 어떻게 할 수 있어?
- Hooks를 통해 Class Component의 Life Cycle Methods 처럼 사용할 수 있음.2. 각각 종류를 알려줘.
- useState, useEffectm useReducer, useMemo, useCallback, useRef, custom Hooks
'> Frontend > React' 카테고리의 다른 글
TIL-2024.05.02 - React - 008. Router (react-router-dom) (0) 2024.05.02 TIL-2024.05.01 - React - 007. CSS-Module & Styled Component (0) 2024.05.02 TIL-2024.04.29 - React - 005. Life Cycle Method (0) 2024.04.30 TIL-2024.04.28 - React - 004. React 에서의 반복 (+추가&제거) (0) 2024.04.28 TIL-2024.04.27 - React - 003. Event Handler (0) 2024.04.27