[React] 11. Hooks (2)

🏃Dekay (JuniorDeveloper)·2021년 9월 30일
0

React

목록 보기
11/13
post-thumbnail

1. hooks ⭐

1.1 useMemo

  • useMemo를 사용하면 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있다.
  • src/Average.js 파일을 생성하여 리스트에 숫자를 추가하면 추가된 숫자들의 평균을 보여주는 함수형 컴포넌트의 코드를 작성했다.
//Average.js
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 = e => {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    };

    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> {getAverage(list)}
            </div>
        </div>
    );
};
  • 하지만 위의 코드는 Input 내용이 수정될 때도 getAverage 함수가 호출된다.
    * 즉, 낭비를 하고 있는 것임.❗
  • 이를 해결하기 위해 useMemo를 사용하여 특정 값이 바뀌었을 때만 연산을 실행하도록 다음과 같이 코드를 수정했다.
//MemoAverage.js
const getAverage = numbers => {
    console.log('평균값 계산 중');
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a, b) => a + b)
    return sum / numbers.length;
};

const MemoAverage = () => {
    const [list, setList] = useState([]);
    const [number, setNumber] = useState('');

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

    const onInsert = e => {
        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>
    );
};
  • 위의 코드를 사용하면 list 배열의 내용이 바뀔 때만 getAverage 함수가 호출된다.

1.2 useCallback

  • useCallback은 앞서 살펴본 useMemo와 비슷한 함수이다.
    * 주로 렌더링 성능을 최적화해야 하는 상황에 사용된다.
  • useCallback hook을 사용하면 이벤트 핸들러 함수를 필요할 때만 생성할 수 있다.
  • 1.1 절 에서 구현한 컴포넌트를 보면 onChangeonInsert라는 함수를 선언했는데, 이는 컴포넌트가 리렌더링될 때마다 함수들이 새로 생성된다.
  • 즉, 컴포넌트의 렌더링이 자주 발생하거나, 렌더링해야 할 컴포넌트의 개수가 많을 경우 useCallback을 사용하여 최적화할 수 있다.
//CallbackAverage.js
const onChange = useCallback(e=> {
  setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성

const onInsert = useCallback(() => {
  const nextList = list.concat(parseInt(number));
  setList(nextList);
  setNumber('');
}, [number, list]); // number || list가 바뀌었을 때만 함수 생성
  • useCallback의 첫 번째 파라미터에는 생성하고 싶은 함수를 넣고, 두 번째 파라미터에는 배열을 넣어 사용한다.
    * 배열에는 어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지 명시해야함.❗❗

TIP ✍

  • 함수 내부에서 상태 값에 의존해야 할 경우 반드시 그 값을 두 번째 파라미터 안에 포함해야한다❗❗
  • 숫자, 문자열, 객체 처럼 일반 값을 재사용 하려면 useMemo를 사용하고, 함수를 재사용 하려면 useCallback을 사용한다고 이해하자.

1.3 useRef

  • useRef Hook은 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해준다.
  • 1.2 절 코드를 활용하여 저번에 해봤던 포커스가 인풋으로 넘어가는 코드는 아래와 같다.
//CallbackAverage.js
const getAverage = numbers => {
    console.log('평균값 계산 중');
    if (numbers.length === 0) return 0;
    const sum = numbers.reduce((a, b) => a + b)
    return sum / numbers.length;
};

const CallbackAverage = () => {
    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>
    );
};
  • useRef를 사용하여 ref를 설정하면 useRef를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가르킨다.

TIP ❗❗

  • 추가적으로 컴포넌트 로컬 변수를 사용해야 할 때도 useRef를 활용할 수 있다.
    * 로컬 변수렌더링과 상관없이 바뀔 수 있는 값이라고 이해하자.❗
  • 하지만, 렌더링과 관련되지 않은 값을 관리할 때만 사용해야 한다.

1.4 커스텀 Hooks 만들기

  • 여러 컴포넌트에서 비슷한 기능을 공유할 경우, Hooks으로 로직을 재사용할 수 있다.
  • 예를들어, [React] 10. Hooks(1)에서 사용했던 InputInfo.js 컴포넌트에서 여러 개의 인풋을 관리하기 위해 작성했던 로직을 CustomInputs라는 Hook으로 따로 분리한 코드는 아래와 같다.
//CustomInputs.js
import { useReducer } from 'react';

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

export default function CustomInputs(initialForm) {
    const [state, dispatch] = useReducer(reducer, initialForm);
    const onChange = e => {
        dispatch(e.target);
    };
    return [state, onChange];
}
  • CustomInputs Hook을 사용하기 위해 src/InputInfo.js를 수정하여 src/CustomInfo.js파일에 코드를 작성했다.
//CustomInfo.js
const CustomInfo = () => {
    const [state, onChange] = CustomInputs({
        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>
    );
};
  • 위와 같이 커스텀 Hooks를 만들어서 사용한 것처럼, 아래의 사이트에서 다른 개발자가 만든 Hooks라이브러리로 설치하여 사용할 수 있다.
    1. react-hooks
    2. awesome-react-hooks
      클래스형 컴포넌트로 프로젝트를 진행하는 것은 문제가 아니지만 그래도 되도록이면 함수형 컴포넌트Hooks를 사용하는 형태로 컴포넌트를 구현하자..❗ 그리고 계속 했던 실수로 렌더링, 함수 호출을 해야할 때 경로를 제대로 설정하지 못했다..
      절대경로, 파일명(대소문자) 구분을 잘하자..😂

      end

profile
Believe you can & you're half way there 🙏

0개의 댓글