[ReactNative] hook (리액트 훅) 알아보기

bi_sz·2023년 11월 2일
0

ReactNative

목록 보기
22/37
post-thumbnail

⭐ Hook ⭐

  • react 16.8 버전에서 새로 소개되었습니다.
  • class를 사용하지 않고도 state와 그 외에도 다른 react의 기능들을 사용할 수 있게 해줍니다.
    -> functional component 에서도 state와 기타 다른 사이드 이펙트 사용 가능

functional component

  • class가 아닌 함수로 component 를 만드는 것 입니다.
  • 원래도 존재하던 component 지만 이전에는 state를 가지지 않고 보이기만 하던 component 로 주로 dumb componet 로만 사용되었습니다.
  • hook이 나오면서 class component 보다 주목받고 있습니다!

hook이 나오게된 과정

  • 컴포넌트간 상태 관련 로직을 재사용하기 어렵습니다.
    -> 매번 같은 상태 관리 로직을 복붙해야했고, 공통화되지 않다보니 컴포넌트마다 변경사항을 수정해야하는 어려움이 있었습니다.
  • 컴포넌트가 커질수록 로직이 복잡해집니다.
    -> componentDidmount 에서 너무 많은 일을 하게됩니다.
    컴포넌트마운트 시 api호출 및 event listener를 가져오는 작업을 한개의 메소드에서 진행하다보니 subscribe 단일 책임 원칙(SRP)에서 벗어나게됩니다.
  • class에서 발생되는 혼란
    -> this 키워드를 사용하면서 prop으로 받아오는 값이 변할 수 있으므로 보장되지 않아 버그를 야기합니다.

💫 hook 사용의 규칙

1. 모든 hook을 사용할 때 최상단에서 호출 하여야만 합니다.

classnew 키워드를 통해서 instance 를 생성하고 instance 안에 있는 render 메소드를 호출하게 되면서 화면을 그리게됩니다.

functional의 경우에는 매번 함수를 호출하여 render 를 동작시킵니다. ( render = 함수호출 )

🌠 이해를 돕기위한 예시코드를 살펴보겠습니다. 🌠

export const ComponentA = () => {
  const [firstName, setFirstName] = useState(''); // 첫 줄에 usestate를 통해 정의
  useEffect(() => {}, []); // 이후에 useEffect 
  const [lastName, setLastName] = useState('');
  useEffect(() => {}, []);
  // ...etc logic
}

react 에서 hook 들은 선언해둔 순서에 따라 state에 접근할 수 있는 index 값이 부여됩니다.

조건부에 따른 hook 호출을 하게된다면 index의 결과값이 달라질 수 있기 때문에 조건부를 타지 않도록 항상 최상단에 모든 hook들을 선언해야합니다.

state를 지칭하고 있는 cursor 값이 있습니다.

hook들이 호출되면 cursorindex가 늘어납니다.

export const ComponentA = () => {
  const [firstName, setFirstName] = useState(''); // [0] cursor index = 0
  useEffect(() => {}, []); 						  // [1] cursor index = 1
  const [lastName, setLastName] = useState('');   // [2] cursor index = 2
  useEffect(() => {}, []); 						  // [3] cursor index = 3
  // ...etc logic
}

index = 1useEffect 를 조건부에 따라 호출하게 된다면,
처음 호출 당시에는 [0], [1], [2], [3] 코드를 실생하여 index0,1,2,3 이지만, 조건부 호출시에는 [0], [2], [3] 의 코드가 index 0, 1, 2 순서로 호출됩니다.

이럴경우에 hook이 갑자기 사라지거나 하기 떄문에 index를 같은 hook이라 볼 수 없습니다. 이런 경우 의도치 않게 버그가 발생됩니다.

2. React 함수에서만 hook을 사용해야합니다.

react 함수 외에 순수함수 등에서 hook을 호출하게되면 error 가 발생합니다.

어떻게보면 당연한거네요.. ㅎㅎ


💫 실무에서 자주 사용하는 library hook

[react-native] useWindowDimensions

export const ComponetA = (props) => {
  const {window, height} = useWindowDimensions();
  {...etc code}
}

화면의 width, height 를 계산하게 될 때 dimensions.getWindow 함수를 매번 함수 컴포넌트 바깥에서 선언하였습니다.

이것을 함수형 컴포넌트 내부에서 hook으로 호출하면 불필요한 코드를 많이많이 줄일 수 있습니다.

useWindowDimensions() 라는 hook 을 바로 사용하지 않고, useScreenSize() 라는 커스텀 hook을 별도로 만들어서 사용하게되면 reactNative 뿐 아니라 react 에서도 적용이 가능합니다.

[react-native] useBackHandler

export const ComponentA = (props) => {
  useBackHandler(() => {
    return true
  });
  {...etc code}
}

Android를 위해서 사용하는 hook 입니다.
하드웨어 Back 키를 누르게 될 때 동작을 처리하는 hook 입니다.
반드시 boolean 값을 return 해주어야합니다.

  • true : eventhandling하고 다음 Component 로 넘기지 않습니다.
  • false : Component 에서 handling하지 않고 다른 Component 제어권을 넘깁니다.
    -> 주로 Modal 을 닫거나 Bottom Sheet 를 닫는 행동을 할 때 사용합니다.
npm install @react-native-community/hooks

react-native package 에 있지 않고
react-native-communityhook package_에 들어가있어 추가로 설치해주어야합니다.

[react-native] useAppState

export const ComponentA = (props) => {
  const currentAppState = useAppState(); // active, background, inactive(only ios)
  {...etc code}
}

현재 APP의 상태가 어떤지 return 해줍니다.

APP의 경우 기본적으로 Foreground 에 있는지, Background 에 있는지 등의 상태가 됩니다.

Foreground 에 있으면 active,
Background 에 있으면 Background 를 값을 return 해줍니다.

요구사항에 따라서 유용하게 사용되는 hook 입니다.

npm install @react-native-community/hooks

useBackHandler 와 마찬가지로 react-native package 에 있지 않고
react-native-communityhook package_에 들어가있어 추가로 설치해주어야합니다.

[react-navigation] useNavigation, useRoute

export const ComponentA = (props) => {
  const navigation = useNavigation();
  const routes = useRoute();
  {...etc code}
}

react-navigation 에서 자주 사용하는 useNavigation, useRoute 입니다.

props에 들어있는 navigation proproute prop 에 접근할 수 있습니다.

매번 propsnavigation proproute prop 를 정의하여 사용할 수 있지만 실무에서는 hook을 자주 사용하기 때문에 hook을 호출하게 되어있습니다.

[react-navigation] uselsFocused, useFocusEffect

export const ComponentA = (props) => {
  const isFocused = uselseFocused();
  useFocusEffect(useCallback(() => {
    //focus가 되었을 때의 처리
  }, [userId]));
  {...etc code}
}

Bottom Tab Navigator 에서 많이 사용됩니다. Tab 혹은 Screen 에 포커싱의 여부를 알고자할 때 사용합니다.

  • uselsFocusedboolean 값을 return 해줍니다.
  • useFocusEffectuseEffect처럼 변경이 되었을 때 callback 을 처리해줍니다.

[react-navigation] useScrollToTop

export const ComponentA = (props) => {
  const scrollViewRef = useRef();
  useScrollToTop(scrollViewRef);
  {...etc code}
}

ScrollView를 최상단으로 올릴 때 사용합니다.

Bottom Tab Navigator 에서 자주 사용됩니다.
Bottom Tab 하단에 있는 아이콘 클릭 시 화면의 최상단으로 이동하는 effect가 필요할 때 작성하게됩니다.

[react-use] useMount

export const ComponentA = (props) => {
  useMount(() => {
    // on mount 처리
  });
  {...etc code}
}

useMountComponentmount 된 시점에 callback 을 호출합니다.

life cicle 에서 에러와 혼동없이 원하는 부분을 짚어내기 위해 사용한다고 합니다.

Componentmount 되는 시점은 useEffect를 통해서도 알 수 있지만, 유지보수를 하다보면 어떤 의미의 useEffect인지 모르는 상황이 발생하거나, 잘 동작하던 코드를 지우게 되면서 버그를 겪게되는 경우가 많다고합니다.

이것을 대비하기 위해 useMount 를 통해 필요한 작업을 한다고 합니다.

[react-use] usePrevious

export const ComponentA = (props) => {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  {...etc code}
}

usePrevious 는 이전에 사용했던 state 값을 알아내고자할 때 사용합니다.

마지막으로 갖고 있던 state를 비교할 일이 생긴다면 유용하겠네요!


지난 게시글 https://velog.io/@bi-sz/ReactNative-Component 에서 Class Componet 들을 만들어두었는데,

다음 게시물에서는 이 컴포넌트들을 functional Component 로 변경해보고 사용할 hook 들은 추가해보도록 하겠습니다!

0개의 댓글