[React] 10. Hooks (1)

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

React

목록 보기
10/13
post-thumbnail

1. Hooks ⭐

Hooks리액트 16.8에 새로 도입된 기능으로 함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect 등의 기능을 제공한다.

  • 실습을 진행하기 위해 create-react-app을 사용하여 hooks 라는 프로젝트를 생성했다.
$ yarn create react-app hooks

1.1 useState

  • useState는 가장 기본적인 Hook이며, 함수형 컴포넌트에서도 가변적인 상태를 지닐 수 있게 해준다고ㅓ 이해하자.
  • 아래의 코드는 src/Counter.js 파일의 코드로 useState 기능을 사용해서 숫자 카운터를 구현했다.
//Counter.js
const Counter = () => {
    const [value, setValue] = useState(0);

    return (
        <div>
            <p>
                현재 카운터 값은 <b>{value}</b>이다.
            </p>
            <button onClick={() => setValue(value +1)}>+1</button>
            <button onClick={() => setValue(value -1)}>-1</button>
        </div>
    );
};
  • useState는 코드 상단에서 import 구문을 통해 불러오고, 다음과 같이 사용한다.
const [value, setValue] = useState(0);
  • useState 함수의 파라미터에는 상태의 기본값을 넣어준다.
    * 여기서 0을 넣은 이유는 카운터 기본값을 0으로 설정
  • 또한, useState가 호출되면 배열을 반환하는데, 그 배열의 첫 번째 원소는 상태 값, 두 번째 원소는 상태 설정하는 함수이다.

TIP ❗❗

  • useState를 여러번 사용할 땐 아래의 코드와 같이 사용하면 된다.
//Info.js
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>
    );
};

1.2 useEffect

  • useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설장하는 Hook 이다.
  • 쉽게 말해 앞서 라이프사이클에서 살펴본 클래스형 컴포넌트componeneDidMountcomponentDidUpdate를 합친 형태로 생각하자.❗
//EffectInfo.js
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 => {
        setName(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>
    );
};

1.2.1 마운트될 때만 실행하고 싶은 경우

  • useEffect에서 설정한 함수를 컴포넌트가 화면에 맨 처음 렌더링될 때만 실행하고, 업데이트될 때는 실행하지 않으려면 함수의 두 번째 파라미터로 비어있는 배열을 넣어주면 된다.
useEffect(() => {
  console.log('마운트될 때만 실행');
  }, []);

1.2.2 특정 값이 업데이트될 때만 실행하고 싶은 경우

  • 특정 값이 변경될 때만 호출하고 싶은 경우는 다음과 같이 사용하면 된다.
useEffect(() => {
  console.log(name);
  }, [name]);
  • 위의 코드와 같이 두 번째 파라미터로 전달되는 배열 안에 검사하고 싶은 값을 넣어주면 된다.

  • 이해를 돕기위해 위의 코드를 클래스형 컴포넌트로 작성하면 아래 코드와 같다.

componentDidUpdate(prevProps, prevState) {
  if(prevProps.value !== this.props.value) {
    doSomething();
  }
}

TIP ❗❗

useEffect렌더링되고 난 직후마다 실행되며, 두 번째 파라미터 배열에 무엇을 넣는지에 따라 실행되는 조건이 틀려지기 때문에 컴포넌트언마운트되기 전이나 업데이트되기 직전에 특정 작업을 수행하고 싶다면 useEffect에서 cleanup 함수를 반환해야 한다.

useEffect(() => {
    console.log('effect');
    console.log(name);
    return () => {
        console.log('cleanup');
        console.log(name);
    };
});

1.3 useReducer

  • useReduceruseState보다 더 다양한 컴포넌트 상태를 다른 값으로 업데이트할 때 사용하는 Hook 이다.
  • 쉽게 말해서 리듀서(Reducer)는 현재 상태와 업데이트를 위해 필요한 정보를 담은 액션(action) 값을 전달받아 새로운 상태를 반환하는 함수이다.
  • 리듀서 함수에서 새로운 상태를 만들 때는 아래의 코드와 같이 반드시 불변성을 지켜야한다.
function reducer(state, action) {
  return {...}; // 👉 불변성을 지키면서 업데이트한 새로운 상태를 반환
}
  • 액션 값은 주로 다음과 같은 형태로 이루어져 있다.
    * useReducer에서 사용하년 액션 객체는 반드시 type을 가질 필요도 없고, 객체가 아닌 문자열이나 숫자여도 상관없다.
{
  type: 'INCREMENT',
  // 👉 다른 값들이 필요하면 추가 (ex. DECREMENT 등등)
}
  • 아래의 코드는 앞서 실습해본 src/Counter.js 파일을 useReducer를 사용하여 새로운 컴포넌트를 생성하는 코드이다.
// ReducerCounter.js
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 ReducerCounter = () => {
    const [state, dispatch] = useReducer(reducer, { value: 0 });

    return (
        <div>
            <p>
                현재 카운터 값은 <b>{state.value}</b>이다.
            </p>
            <button onClick={() => dispatch({ type: 'INCREMENT'})}>+1</button>
            <button onClick={() => dispatch({ type: 'DECREMENT'})}>-1</button>
        </div>
    );
};
  • useReducer의 첫 번째 파라미터에는 리듀서 함수를 넣고, 두 번째 파라미터에는 해당 리듀서의 기본값을 넣어 사용한다.
  • 위의 Hook을 사용하면 state 값과 dispatch 함수를 받아오는데, 여기서 state는 현재 가르키고 있는 상태이고, dispatch액션을 발생시키는 함수이다.
    * 즉, dispatch(action)의 형태로 리듀서 함수가 호출되는 구조

TIP ❗❗

  • useReducer를 사용하면 컴포넌트 업데이트 로직을 컴포넌트 바깥(?)으로 빼낼 수 있다.

1.3.1 인풋 상태 관리하기

  • 아래의 코드는 useReducer를 사용하여 Input 상태를 관리하는 코드이다.
  • 기존에는 Input이 여러 개여서 useState를 여러 번 사용했는데, useReducer를 사용하면 기존에 클래스형 컴포넌트에서 input 태그에 name 값을 할당하고, e.target.name을 참조해서 setState를 해 준 것과 비슷하게 처리할 수 있다.
//InputInfo.js
function reducer(state, action) {
    return {
        ...state,
        [action.name]: action.value
    };
}

const InputInfo = () => {
    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>
    );
};
  • useReducer에서 액션은 어떤 값을 사용하든 상관없다.
  • 위의 코드는 이벤트 객체가 지니고 있는 e.target 값 자체를 액션 값으로 사용한 코드이다.

Hook을 시작하면서 이제 드디어 리액트를 맛보는 느낌이다.
아직 조금 어색하긴 하지만 target, dispatch, reducer 헷갈리지 않게 반복적으로 공부하자.😊❗

end

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

0개의 댓글