[React를 다루는 기술] 8장 - Hooks

3 minute read

😉 useState

  • 첫 번째 인자로 현재 상태, 두 번째 인자로 상태를 바꾸는 함수
  • 상태를 변화시킬 땐 항상 불변성 유지
import React, { useState } from "react";

export default function Counter() {
  const [value, setValue] = useState(0);

  return (
    <div>
      <h1>The number is {value}</h1>
      <button
        onClick={() => {
          setValue(value + 1);
        }}
      >
        +1
      </button>
      <button
        onClick={() => {
          setValue(value - 1);
        }}
      >
        -1
      </button>
    </div>
  );
}

😉 useEffect

  • useEffect( function, deps? );
  • function : useEffect가 수행 될 때 실행되는 함수
  • deps [optional] : 배열 형태이며, 의존(dependency) 값을 의미한다.

deps[optinal] 값의 의미

  1. 마운트 될때, 업데이트 될때 - 값이 없을 경우
  • useEffect(()⇒{})
  • 화면이 렌더링 된 이후 수행이 되며, 리 렌더링이 발생하는 경우 다시 수행이 된다
  1. 마운트 될 때만 쓰임 - 빈 배열 인경우
  • useEffect(()⇒{}, [])
  • 화면이 렌더링 된 이후에만 수행이 된다.
  1. 특정값이 업데이트 될 때만 실행 - 배열 값이 존재하는 경우
  • useEffect(()⇒{}, [값])
  • 화면이 렌더링 된 이후에 수행이 되고, ‘값’이 변경되었을 경우 해당 메서드가 수행이 된다.
  1. deps에 특정 값을 넣게 된다면 컴포넌트가 처음 마운트 될 때, 지정한 값이 바뀔 때, 언마운트 될 때, 값이 바뀌기 직전에 모두호출이 된다.

💡clean-up 함수

  • Component의 unmount이전 / update직전에 어떠한 작업을 수행하고 싶다면 Clean-up함수를 반환해 주어야 한다.

    • unmount 될 때 : useEffect(func, [])
    • 특정값 update 직전 : useEffect(func, [특정값])
  • clean-up함수를 사용하게되면, 작동 순서는

    • re-render -> 이전 effect clean-up -> effect
useEffect(() => {
  console.log("일반 상황1 -> 마운트되어서 Info 컴포넌트가 보일 때");
  console.log("일반 상황2 -> 정보가 업데이트 될 때");
  return () => {
    console.log("뒷정리 함수 상황1 : 언마운트 되어서 숨기려할 때");
    console.log(
      "뒷정리 함수 상황2 -> 업데이트 되기 직전에. 즉 input에 값 입력해도 업데이트 직전인 상황나옴. 리액트 -> 리액ㅌ"
    );
    console.log("상황 2는 2번째 배열에 값에 따라 달라짐");
  };
}, [name]);

😉 useReducer

  • 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트 해주고 싶을 때
  • 지금까지는 onClick 이벤트 같은 누르는 상황에만 값을 업데이트 했음.
  • reducer(state, action). (현재 상태, 업데이트를 위해 필요한 정보를 담은 액션)
  • action 값을 받아 새로운 상태로 변환 -> 불변성 유지
  • 어떤 action인지 알려주는 type 필드 필요
  • 컴포넌트 외부에 함수로 설정
  • useReducer의 가장 큰 장점 : 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낸다.
function reducer(state, action) {
  switch (action.type) {
    case "INCREASE":
      return { value: state.value + 1 };
    case "DECREASE":
      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: "INCREASE" })}>+1</button>
      <button onClick={() => dispatch({ type: "DECREASE" })}>-1</button>
    </div>
  );
};

😉 useMemo

  • 컴포넌트 내부에서 발생하는 연산 최소화
  • 실습 코드는 insert외에 input 내용 수정되어도 getAverage함수가 호출됨
  • useMemo 사용하면 특정 값이 바뀌었을 때만 연산을 실행, 원하는 값이 바뀌지 않았다면 이전 연산 결과를 다시 사용

😉 useCallback

  • 만들어 놨던 함수 재사용
  • 렌더링 성능 최적화
  • 그냥 함수 선언하면 렌더링 할 때마다 함수 다시 렌더링
  • useCallback 사용하면 처음 렌더링 했을 때 만들어 놓은 함수 리렌더링 시 다시 안만들고 재사용
  • 두 번째 인자로 업데이트 된 상태 적용하면, 그때만 리렌더링할 때 함수 다시 생성
import React, { useState, useMemo, useCallback } from "react";

const getAverage = (numbers) => {
  console.log("calculating now");
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

export default function Average() {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState("");

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

  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number, 10));
      setList(nextList);
      setNumber("");
    },
    [number, list]
  );

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

  return (
    <div>
      <h1>Average</h1>
      <input value={number} onChange={onChange} />
      <button onClick={onInsert}>Enroll</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>평균값:</b> {avg}
      </div>
    </div>
  );
}

😉 useRef

  • ref 기능을 사용하고자 할 때 쓸 수 있다.
  • 컴포넌트의 로컬 변수를 사용해야 할 때도 사용할 수 있다.
  • useRef의 객체는 렌더링에 영향을 미치지 않는다.
const inputEl = useRef(null);

const onInsert = useCallback(e => {
  ...
  inputEl.current.focus();
})

<input value={number} onChange={onChange} ref={inputEl} />
<button onClick={onInsert}>등록</button> //dom에 접근

const id = useRef(1);
const setId = (n) => {
  id.current = n;
}
const printId = () => {
  console.log(id.current) // 로컬 변수 (렌더링과 상관없이 바뀔 수 있는 값) 사용하기
}

😉 custom hooks

import { useContext } from "react";
import RouterContext from "../components/RouterContext";

const useRouter = () => {
  const { setPath } = useContext(RouterContext);
  const push = (path) => {
    history.pushState({}, "", path);
    dispatchEvent(new Event("popstate"));
    setPath(path);
  };
  return { push };
};
export default useRouter;

Leave a comment