본문 바로가기
Frontend/ReactJS(완)

React - 8장 : React 자주 쓰이는 Hooks 총 정리

by VictorMeredith 2023. 2. 15.
<리액트를 다루는 기술>

React - 1장 : React 이해

React - 2장 : JSX

React - 3장 : 컴포넌트

React - 4장 : 이벤트 핸들링

React - 5장 : ref. DOM에 이름 달기 

React - 6장 : 컴포넌트 반복
React - 7장 : 컴포넌트의 LifeCycle
React - 8장 : React Hooks 총정리 (현재)

Hooks는 v16.8에 도입된 기능으로 함수컴포넌트에서도 useState, useEffect등의 기능을 제공하여 다양한 작업을 할 수 있도록 해준다.

8-1. useState

- 함수 컴포넌트에서 가변적인 상태를 지닐 수 있도록 해준다.

import {useState} from 'react';

const Counter = ()=>{
    const [value, setValue] = useState(0); //useState안에는 state의 초기값(기본값)을 넣어준다.
    //value : state 그 자체
    //setValue : state 변경함수 (파라미터에 변경될 값을 직접 입력해줘야한다.)

    return (
        <div>
            <p>
                현재 카운터의 값은 {value}입니다. //data binding
            </p>
            <button onClick={()=>{setValue(value+1)}}>+1하기</button> // onClick 이벤트핸들링
        </div>
    )
}

export default Counter;

- 컴포넌트에서 관리할 상태가 여러개라면 변수 작성하듯이 여러번 작성하면 된다.

- cf) useState함수를 호출하면 두개의 요소를 가진 배열을 return하는데 이를 구조분해할당문법으로 변수에 각각 할당한다

const [name, setName] = useState('어나니머스');
const [nickName, setNickName] = useState('닉네임없는데');

 

8-2. useEffect

- 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook.

- class형 컴포넌트의 componentDidMount와 componentDidUpdate를 합친 것이다.

const MyComponent = ()=>{

    useEffect(()=>{
        console.log('렌더링이 완료되었습니다.')
        console.log('주로 ajax나 DOM컨트롤할 때 쓰인다.')
    })

    return (
		...
    )
}

export default MyComponent;

8-2.1. useEffect - 마운트될 때만 실행하고 싶을  때

    useEffect(()=>{
    	console.log('두번째파라미터로 [] 넣어준다')
    },[])

 

8-2.2. useEffect - 특정 값이 업데이트될 때만 실행하고 싶을 때

    useEffect(()=>{
    	console.log('value가 수정될때만 실행하자')
    },[value])

8-2.3. useEffect - 뒷정리하기

- useEffect는 기본적으로 렌더링이되고 난 직후마다 실행되며, 두 번째 파라미터 배열에 무엇을 넣는지에 따라 실행되는 조건이 달라진다. 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 작업을 수행하고 싶다면, useEffect에서 뒷정리함수를 반환해 주어야 한다.

 

    useEffect(()=>{
        console.log('effect');
        console.log(value);
        return ()=>{ //뒷정리함수 반환
            console.log('cleanup')
            console.log(value);
        }
    },[value])

 

 

- 컴포넌트가 나타날 때 콘솔에 effect가 나타나고, 사라질 때 cleanup이 나타난다.

- 오직 언마운트 될 떄만 뒷정리 함수를 호출하고 싶다면, useEffect 함수의 두 번째 파라미터에 비어있는 배열을 넣으면 된다.

 

8-3. useReducer

- useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트해 주고 싶을 경우 사용

- reducer의 개념은 17장에서 다시 다룰 예정.

- 리듀서는 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 action 값을 전달받아 새로운 상태를 반환하는 함수.

- 리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야 한다.

function reducer(state, action){
    return { ... }; // 불변성을 지키면서 업데이트한 새로운 상태를 반환
}

 

- action 값은 이렇게 생겼다.

{
    type : 'INCREMENT',
    // ~~~
}

- 리덕스에서 사용하는 액션 객체에는 type field가 반드시 있어야하지만, useReducer에서는 type을 지니고 있을 필요는 없다. 

8-3.1. useReducer - 카운터 구현하기

Counter.jsx

import {useReducer} from 'react';

function 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 = ()=>{
    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;

- useReducer의 첫 번째 파라미터에는 리듀서 함수를 넣고, 두 번째 파라미터에는 해당 리듀서의 기본값을 넣어준다.

- 이 Hook을 사용하면 state 값과 dispatch 함수를 받아오는데, state는 현재 가리키고 있는 상태이고, displatch는 액션을 발생시키는 함수이다.

- dispatch(action)의 형태로 호출하면 리듀서 함수가 호출되는 구조이다.

- useReducer의 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것이다.

 

8-3.2. useReducer - 인풋 상태 관리하기

Info.jsx

import { useReducer } from "react";

function reducer(state, action){
    return {
        ...state,
        [action.name]: action.value
    }
}

const Info = ()=>{
    const [state, dispatch] = useReducer(reducer, {
        name : '',
        nickname : ''
    });
    
    const { name, nickname } = state;

    const onChange = e => {
        dispatch(e.target);
    };

    return (
        <div>
            <div>
                <input type="text" name="name" value={name} onChange={onChange} />
                <input type="text" name="nickname" value={nickname} onChange={onChange} />
            </div>
            <p>이름 : {name}</p>
            <p>닉네임 : {nickname}</p>
        </div>
    )
}

export default Info;

- useReducer에서의 액션은 어떤 값이라도 사용이 가능

- e.target 값 자체를 액션 값으로 사용한 예제

- Input의 개수가 많아지더라도 코드를 간결하게 유지가 가능하다.

 

8-4. useMemo

- useMemo를 사용하면 함수 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있다.

- useEffect와 유사하다.

const getAverage(numbers){
//숫자배열을 파라미터로 받아 평균값을 계산하는 로직
}

const 평균 = useMemo(()=>getAverage(list),[list])

- 이렇게 하면 list 배열의 내용이 바뀔 때에만 getAverage함수가 호출되므로, 리소스를 절약할 수 있다.

 

8-5. useCallback

- useMemo와 유사하다.

- 렌더링 성능을 최적화해야 하는 상황에서 사용한다. : 렌더링이 너무 잦거나 개수가 많아지면 최적화 용도로 사용

- 만들어놨던 함수를 재사용 할 수 있다. 

    const [number, setNumber] = useState('');
    const onChange = useCallback(e=>{
        setNumber(e.target.value);
    }, []) //컴포넌트가 처음 렌더링될 때만 함수 생성

- useCallback의 첫 번째 파라미터에는 생성하고 싶은 함수를 넣고, 두 번째 파라미터에는 배열을 넣는다.

- 이 배열에는 어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지 명시한다.

- 함수 내부에서 어떠한 상태 값에 의존해야 할 때는 그 값을 반드시 두 번째 파라미터 안에 포함시켜 주어야 한다.

  ex) 기존의 state를 조회해서 새로 생성하는 함수의 경우

 

8-6. useRef

- 함수 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해준다.

import { useState, useRef } from "react";

const Test = ()=>{
    const [nowSwitch, setNowSwitch] = useState(false);
    const pEl = useRef(null);
    const onOffSwitch = ()=>{
        console.log(pEl.current); //HTML DOM ELEMENT
        pEl.current.style.color = 'red'
        setNowSwitch(!nowSwitch);
    }
    return (
        <div>
            <p ref={pEl}>{nowSwitch ? 'On!' : 'Off!'}</p>
            <button onClick={onOffSwitch}>{nowSwitch ? 'Off하기' : 'On하기'}</button>
        </div>
    )
}

export default Test;

- useRef를 사용하여 ref를 설정하면 useRef를 통해 만든 객체 안의 current 값이 실제 element를 가리킨다.

8-6.1. useRef - 로컬 변수 사용하기

- 렌더링과 상관없이 바뀔 수 있는 값인 컴포넌트 로컬변수를 사용해야 할 경우 useRef를 활용할 수있다.

import { useState, useRef } from "react";

const Sample = ()=>{

    const RefSample = ()=>{
        const id = useRef(1);
        const setId = n =>{
            id.current = n;
        }
    }

    const printID = ()=>{
        console.log(id.current)
    }

    return (
        <div>
            RefSample
        </div>
    )
}

export default Sample;

- ref 안의 값이 바뀌어도 컴포넌트가 렌더링 되지 않는다는 점을 주의한다.

- 렌더링과 관련되지 않은 값을 관리할 때만 이러한 방식으로 코드를 작성하면 된다.

 

8-6. Custom Hooks

- 여러 컴포넌트에서 비슷한 기능을 공유할 경우 커스텀 훅으로 로직을 재사용 할 수 있다.

CustomHook.jsx

export default function useCustomFunction(){
    //Do something2
}

Info.jsx

import useCustomFunction from './CustomHook.jsx'

useCustomFunction();

- 깔끔하게 정리할 수 있다.

- 또한 다른 개발자가 만든 다양한 Hooks를 사용할 수 있다.

 

댓글