개발자로 살아남기-캐나다

리액트 훅(React hook)이란? 예제로 알아보기 본문

Reactjs

리액트 훅(React hook)이란? 예제로 알아보기

志者必得 2021. 5. 19. 11:28

이번 글에서는 리액트에서 제공하고 있는 훅에대해서 설명하고 어떻게 사용하는지 예시를 보면서 배워 보겠습니다. 

앞에 글에서 리액트 훅이 왜 나오게 되었고 어떻게 사용되는지 간단하게 알아보았습니다. 앞에글을 읽어 보시면 더 이해가 잘 될 것이라 생각됩니다.

 

useState

클래스 형태의 컴포넌트에서 사용하던 state와 setState를 사용할 수 있게 해주는 함수 입니다.

useState은 기본 값을 받을 수 있고 현재 state와 state를 업데이트 할 수 있는 함수를 리턴합니다.

const [showList, updateShowListState] = useState(false) // false는 기본값
const [todoData, updateTodoData] = useState({ todos: [], currentIndex: 0 })

위에서 보는 것 처럼 useState에 어떤 값을 넣으면 그 값이 기본 값이 된다 예를들어 showList에 기본 값은 false가 되는 것이다. 그리고 만약에 showList에 데이터를 업데이트 하고 싶다면 간단하게 updateShowListState(true)와 같이 원하는 값을 넣어주면 된다. 

 

클래스 형태의 컴포넌트와 같이 컴포넌트에 귀속되어 있는 데이터를 저장할 때 사용된다. 예를들어 유저가 매뉴를 클릭했는지 않했는지, 현재 마우스 포지션 값을 저장할 때 사용하면 된다.

 

 

useEffect

리액트 라이프 사이클을 사용할 수 있게 해주는 함수 입니다. 훅을 통해서 사용하는 리액트 라이프 사이클에는 componentDidMount, componentDidUpdate, componentWillUnmount이 있습니다.

 

클래스에서 그리고 훅에서 라이프 사이클을 비교해 보겠습니다.

아래 보이는 것 처럼 맨 처음에 컴포넌트가 마운트 됐을 fetchArea를 부르고 윈도우 이벤트를 추가 해줍니다.

//클래스

  componentDidMount() {
    fetchArea()
    // window event
    window.addEventListener('scroll', this.handleScroll)
  }
  
  componentDidUpdate(prevProps, prevState) {
    if(prevProps.selecteArea !== this.props.selectedArea) {
      fetchArea()
    }
  }

  componentWillUnmount() {
    // clean up
    window.removeEventListener('scroll', this.hanldeScroll)
  }

 

리액트 훅으로 같은 코드는 어떻게 될까? 아래와 같이 변한다 componentMount와 componentDidUpdate는 useEffect 하나로 다 커버가 되고 componentWillUnmount 또한 useEffect에서 리턴하는 함수 안에 로직을 넣어주면 같은 기능을 한다.

const Area = ({ fetchArea, selectedArea, area }) => {
  // componentDidMount, componentDidUpdate와 같은 기능을 할 수 있음
  useEffect(() => {
    window.addEventListener("scroll", this.handleScroll);
    // useEffect에서 함수는 우리가 componentWillUnmount에서 했던 것 처럼
    // 컴포넌트가 언마운트 될 때 필요한 로직을 넣어주면 된다.
    return () => window.removeEventListener("scroll", this.hanldeScroll);
    // 의존하는 변수가 없다면 컴포넌트가 마운트 됐을 때 딱 한번 실행
  }, []);

  useEffect(() => {
    fetchArea();
    // 컴포넌트가 마운트 됐을 때 1번
    // 의존하는 변수가 있다면 변수가 바뀔 때마다 다시 실행
  }, [selectedArea]);

  return <div />;
};

클래스 형식의 컴포넌트 전체적으로 깔끔하고 로직을 따로 따로 분리 하기가 쉽다. 또한 componentDidUpdate 같이 prev.propsName !== this.props.propName 이런 것도 안해줘도 되기 때문에 더 깔끔하고 편하다.

 

useMemo

리액트에서는 prop이 바뀌면 다시 랜더링을 해주는데 그때 마다 아래와 같은 경우에는 calculateArea를 항상 실행 지켜 준다. 

예를들어 prop으로 마우스 포지션을 받는다고 하면 마우스를 움직일 때 마다 calculateArea 함수를 실행 시킬 텐데 이것은 함수에 하는 일에 따라 엄청난 과부하를 줄 수도 있다. 

const Area = ({ selectedArea, mousePosition }) => {
  const calculatedArea = calculateArea(selectedArea)
  return <div />;
};

useMemo를 사용하게 되면 함수를 실행하는데 필요한 prop이 바뀔 때만 calculateArea를 실행 할 수 있다.

// useMemo(값을 리턴하는 함수, 의존하고 있는 변수)
const calculatedArea = useMemo(() => calculateArea(selectedArea), [selectedArea])

위와 useMemo를 사용하면 selectedArea가 변할 때만 calculateArea를 실행하게 된다.

 

useCallback

useMemo는 값을 캐싱 했다면 useCallback은 함수를 캐싱 해주는 리액트 훅이다. 이런 식으로 사용하면 selectedArea가 변하지 않는 이상 새로운 함수를 리턴하지 않는다.

useCallback(() => calculateArea(selectedArea), [selectedArea]);

useRef

useRef에 사용방법은 크게 2가지로 나눠 질 수 있습니다.

 

1. useRef은 리액트에서 dom에 직접 관여해야 할 때

리액트에서는 querySelector등을 통해서 dom을 가져와서 조작하는 것을 안티 패턴으로 보고 있습니다. 이렇게 하면 버추얼 돔을 사용하는 이점이 없어지게 되는 것이고 업데이트도 제대로 되지 않을 수 있습니다. 그래서 ref를 가져와야 하는데 dom ref를 가져오는 방법은 아래와 같습니다. inputRef를 ref prop으로 넘겨주면 컴포넌트가 마운트 되었을 때 자동으로 ref를 가져옵니다.

const inputRef = useRef(null)
// <input ref={inputRef} /> 

useRef는 항상 { current } 오브젝트를 리턴합니다. 그렇기 때문에 input에 ref에 접근하기 위해서는 inputRef.current와 같이해야 합니다.

 

예를들어 input에 focus를 주고 싶다면 아래 처럼 current.focus를 해주면 됩니다.

inpuRef.current.focus()

 

2. 랜더링과 상관 없이 데이터 업데이트 하기.

 

리액트에서는 state나 props이 변경되면 컴포넌트는 re-rendering을 하게 됩니다. 만약에 어떤 값이 바뀌었을 때 컴포넌트를 리랜더링 시키고 싶지 않다면 ref를 사용하면 됩니다.

// useRef에 기본 값을 넘겨 줄 수 있습니다.
const clickCount = useRef(0)

// clickCount.current === 0

 

clickCount를 state로 관리하게 되면 clickCount가 올라갈 때 마다 랜더링을 하게 되는데. ref.current에 데이터를 저장하게 되면 랜더링이 일어나지 않는다. 

 

 

0 Comments
댓글쓰기 폼