React Native Hooks

silver·2021년 7월 24일
0

Hooks

Hooks는 React Native 0.59에 추가되었습니다. 클래스없이 state와 기타 React 기능을 사용할 수 있게합니다.

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useState 함수가 알아볼 처음 "Hook"이다.

State Hook

이 예제는 counter를 렌더링한다. 버튼을 눌렀을때, 값이 증간한다.

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useState가 Hook이다. 함수 컴퍼넌트 내부에서 호출하여 local state를 추가한다. React는 재렌더 간의 state를 보존한다. useState는 쌍을 반환한다: 현재 state 값과 그 값을 갱신할 수 있도록 하는 함수. 이벤트 핸들러나 다른 곳에서 이 함수를 호출할 수 있다. 클래스에서 사용하는 this.setState와 비슷하다, 이전 state와 새로운 state를 함께 합친다는것을 제외하면 말이다.

useState인수는 초기 state 값이다. 위의 예제에서, counter를 0부터 시작하기 때문에 0이 초기값이다. this.state와 달리, state는 object일 필요가 없다 - 원한다면 할 수 있다. 초기 state 인자는 처음 렌더링할때만 사용된다.

여러개의 State 변수 선언하기

단일 component에서 한개 이상의 State Hook을 사용할 수 있다.

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

array destructuring 구문을 사용하면 useState를 호출하여 선언한 state 변수에 다른 이름을 지정할 수 있다. 이 이름들은 useState api에 포함되지 않는다. 대신에, React는 useState를 여러번 호출할 경우 모든 렌더에서 동일한 순서로 호출한다고 가정한다. 이 기능이 작동하는 이유와 언제 유용한지 나중에 다시 살펴보도록 하겠다.

그래서 Hook이 뭐지?

Hooks는 함수 컴퍼넌트의 라이프사이클과 React state를 "연결"할 수 있게하는 함수입니다. Hooks는 클래스 안에서는 동작하지 않습니다 - 클래스 없이도 React를 사용할 수 있도록 합니다.

React는 useState와 같은 몇가지 정의된 Hooks를 제공합니다. 다른 컴퍼넌트간에 state적인 행동을 재사용하는 자신만의 Hooks를 만들 수 있습니다. 먼저 미리 정의된 Hooks를 살펴보도록 하겠다.
(state hook과 관련하여 더 자세한게 또 있다고 하는데.. 뭘이렇게 파고파고파고또파고..힘들어 죽겠네)

Effect Hook

이전에 React component에서 data fetching, subscriptions 또는 수동으로 DOM 변경을 한적이 있을 것이다. 이러한 작업들은 다른 컴퍼넌트에 영향을 미칠수 있고 렌더링 중에는 수행할 수 없기 때문에 "side effects"(짧게는 "effects")라고 한다.

Effect Hook(useEffect)는 함수 컴퍼넌트의 side effect를 수행하는 기능을 추가한다. 이것은 React 클래스에서 componentDidMount, componentDidUpdat, componentWillUnmount와 같은 용도로 사용한다, 하지만 이게 단일 API로 통합되었다.

예를 들어, 이 컴퍼넌트는 React가 DOM을 업데이트한 후 document title을 설정한다:

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect를 호출하면, DOM에 변경 사항을 플러시한 후 React에 "effect"함수를 실행하라는 것이다. effect는 컴퍼넌트안에 선언되어 있다 그래서 props와 state에 접근할 수 있다. 기본적으로, React는 첫번째 렌더를 포함하여 모든 렌더 후에 effects를 실행한다.

effects는 함수를 반환하여 "cleau up"하는 방법을 선택적으로 지정할 수 있다. 예를 들어, 이 컴퍼넌트는 effect를 사용하여 친구의 온라인 상태를 구독하고, 구독을 취소하여 정리한다.

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

이 예제에서, React는 컴퍼넌트가 그 다음의 렌더로 인해 effect를 재실행하기 전인 unmount일 때 ChatAPI로 구독취소를 할 수 있다.(만약 원한다면, ChatAPI를 통해 전달할 props.frined.id가 변하지 않았다면 React가 재구독하는것을 피하게 할 수 있다.)

useState와 마찬가지로, 컴퍼넌트에서 두개 이상의 effect를 사용할 수 있다:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...

Hooks를 사용하면 라이프사이클 메소드에 따라 강제로 분할하는 대신, (구독 추가 및 제거와 같은) 관련 부분을 기준으로 컴퍼넌트의 side effects를 구성할 수 있다.

Hooks의 규칙

Hooks은 Javascript 함수이다, 하지만 두가지 추가 규칙이 적용된다.

  • Hooks은 최상위 레벨에서 호출할 수 있다. loops, conditions, nested functions 에서 hooks을 호출하면 안된다.
  • Hooks은 React function components에서만 호출할 수 있다. 일반적인 JavaScript 함수에서 호출하면 안된다. (Hooks을 호출할 수 있는 곳이 하나있다 - 커스텀 Hooks. 이건 나중에 배우도록 하겠다.)

이러한 규칙을 자동으로 적용할 수 있는 linter plugin을 제공한다. 처음에는 이러한 규칙이 제한적이거나 혼란스러울 수 있다는 것을 이해하지만, Hooks이 잘 작동하도록 하는데는 필수적이다.

자신만의 Hooks 구성하기

때때로, 우리는 component간에 stateful logic을 재사용하길 원한다. 전통적으로, 이문제에 대한 두가지 해결책이 있다: higher-order componentsrender props. 커스텀 Hooks는 트리에 component를 추가하지 않고도 이작업을 수행할 수 있다.

위에서, 친구의 온라인 상태를 구독하기위한 useEffectuseState Hooks를 호출하는 FriendStatus 컴퍼넌트를 소개했다. 이 구독 로직을 다른 컴퍼넌트에서도 재사용한다고 해보자.

먼저, 우리는 이 로직을 userFriendStatus라고 불리는 커스텀 Hook으로 추출할 것이다.

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

friendID를 인자로 받고, 친구가 온라인인지 아닌지를 반환한다.

이제 두 컴퍼넌트에 이걸 써보도록 하겠다.

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

각 컴퍼넌트의 state는 완전히 독립적이다. Hooks는 stateful logic을 재사용할 수 있도록하는 방법이다. 사실, Hook에 대한 각 호출은 완전히 분린된 state를 가진다. - 그래서 한 컴퍼넌트에서 같은 커스텀 Hook를 두번이나 사용할 수도 있다.

커스텀 Hooks는 기능보다는 규약에 가깝다. 만약 함수 이름이 "use"로 시작하고 다른 Hooks를 호출한다면, 그것도 커스텀 Hook라고 한다. useSomething 명명 규칙은 linter plugin이 Hooks를 사용하여 코드에서 버그를 찾을 수 있는 방법이다.

타이머, 애니메이션, 등등 우리가 고려하지 않은 다양한 사용 사례를 포함하는 커스텀 Hooks를 작성할 수 있다.

다른 Hooks

유용할 수 있는 몇가지 덜 자주 사용되는 기본 제공 Hook가 있다. 예를 들어, useContext는 중첩을 도입하지 않고 React context를 subscribe할 수 있다:

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}

useReducer를 사용하면 reducer로 복잡한 컴퍼넌트의 로컬 state를 관리할 수 있다.

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...

끝!

출처: https://reactjs.org/docs/hooks-overview.html

0개의 댓글