TIL | #11 React | Hook 함수 useReducer, useMomo, useCallback

trevor1107·2021년 4월 11일
0

2021-04-02(금)

useReducer

useState보다 다양한 컵포넌트 상황에 따라 상태를 다른 값으로 업데이트 해주고 싶을 때 사용한다.

Reducer -> 현재 상태, 업데이트를 위해 필요한 정보를 담은 값(action)을 전달받아 새로운 상태를 반환하는 함수이다.

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

// 액션 객체
{
  type : 'actionType'
}

리덕스에서 사용하는 액션 객체에는 반드시 어떤 액션인지 알려주는 type필드가 꼭 있어야 한다.

useReducer에서 사용하는 액션객체는 반드시 type을 지니고 있을 필요는 없다.

굳이 객체가 아니라 문자열이나 숫자여도 상관 없다.

useReducer를 사용했을 때 컴포넌트 업데이트 로직을 바깥으로 빼낼 수 있는게 장점이다.

// Counter.js

import React, { useReducer, useState } from 'react';
function reducer(state, action) {
    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 });
    // state : 현재 가리키고 있는 상태
    // dispatch : 액션을 발생시키는 함수

    return (
        <div>
            <p>
                현재 카운터 값은 <b>{state.value}</b>
            </p>
            {/* dispatch(action) 함수 안에 매개변수가 전달되면 reducer함수가 호출 된다.*/}
            <button onClick={() => dispatch({ type: 'INCREMENT' })}> +1</button>
            <button onClick={() => dispatch({ type: 'DECREMENT' })}> -1</button>
        </div>
    );
};

export default Counter;
// Info.js

import React, { 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 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;

useMemo

메모이제이션 된 값을 반환하는 함수이다.

메모이제이션(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다. 동적 계획법의 핵심이 되는 기술이다. 메모아이제이션이라고도 한다.

함수형 컴포넌트 내부에서 발생하는 연산을 최적화 한다.
렌더링 하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행한다.
원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용하는 방식이다.

// Average.js

import React, { useCallback, useMemo, useState } from 'react';

const getAverage = function (numbers) {
    console.log('평균 값 계산중...');
    if (1 > numbers.length) 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 = function (e) {
        setNumber(e.target.value);
    };
    const onInsert = function (e) {
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
    };

    const avg = useMemo(() => getAverage(list), [list]);

    return (
        <div>
            <input vlaue={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;

useCallback

렌더링 성능을 최적화해야하는 상황에서 사용한다.

만들어 놓은 함수를 재사용할 수 있다.

첫번째 매개변수에는 생성하고 싶은 함수를 넣고 두번째 매개변수에는 배열을 넣는다.

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

함수 내부에서 상태 값에 의존해야하는 경우 그 값을 반드시 두번째 매개변수 안에 포함시킨다.

// Average.js

import React, { useCallback, useMemo, useState } from 'react';

const getAverage = function (numbers) {
    console.log('평균 값 계산중...');
    if (1 > numbers.length) 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) => {
        console.log('useCallback onChange');
        setNumber(e.target.value);
        // 기존의 값을 조회하지 않고 바로 설정만 하기때문에
        // 배열이 비어있어도 된다.
    }, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성

    const onInsert = useCallback(() => {
        console.log('useCallback onInsert');
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
        // 기존의 값을 조회해서 nextList를 생성하기 때문에
        // 배열안에 number, list를 넣어주야아 한다.
    }, [number, list]); // number 혹은 list가 바뀌었을 때만 함수 생성

    const avg = useMemo(() => getAverage(list), [list]);
    return (
        <div>
            <input vlaue={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;

useRef

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

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

// Average.js

import React, { useCallback, useMemo, useRef, useState } from 'react';

const getAverage = function (numbers) {
    console.log('평균 값 계산중...');
    if (1 > numbers.length) 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 inputElement = useRef(null);

    const onChange = useCallback((e) => {
        console.log('useCallback onChange');
        setNumber(e.target.value);
    }, []);

    const onInsert = useCallback(() => {
        console.log('useCallback onInsert');
        const nextList = list.concat(parseInt(number));
        setList(nextList);
        setNumber('');
        inputElement.current.focus();
    }, [number, list]);

    const avg = useMemo(() => getAverage(list), [list]);
    return (
        <div>
            <input value={number} onChange={onChange} ref={inputElement} />
            <button onClick={onInsert}>클릭</button>
            <ul>
                {list.map((value, index) => (
                    <li key={index}>{value}</li>
                ))}
            </ul>
            <div>
                <b>평균 값 : </b>
                {avg}
            </div>
        </div>
    );
};
profile
프론트엔드 개발자

0개의 댓글