[처음부터 배우는 리액트 네이티브]Hooks

최다연·2025년 11월 21일

ReactNative

목록 보기
4/8

Hooks

이전에는 컴포넌트의 상태관리나 생명주기에 따라 작업을 수행할 때에는 클래스형 컴포넌트만 사용해야 했다. 그러나 Hooks이 등장하며 함수형 컴포넌트에서도 상태를 관리할 수 있게 되었다.

useState

useState를 호출하면 변수와 그 변수를 수정할 수 있는 새터 함수를 배열로 반환한다. 관리해야 하는 상태의 수만큼 여러번 사용할 수 있다. 상태 관리 변수는 반드시 새터 함수를 이용해 값을 변경해야 한다.

새터 함수를 사용하는 방법은 두가지가 있다.

  1. 새터 함수에 변경될 상태의 값을 전달하기

  2. 새터 함수의 파라미터에 함수를 전달하기

    setState(prevState => {});

    새터 함수는 비동기로 동작하기 때문에 상태 변경이 여러번 일어나면 상태가 변경되기 전에 또다시 상태에 대한 업데이트가 실행되는 상황이 발생한다. 이럴 땐 새터함수에 함수를 인자로 전달하여 이전 상태값을 이용한다.

<Button
	title="+"
	onPress={() => {
		setCount(count + 1);
		setCount(count + 1);
	}}
/>
<Button
  title="+"
  onPress={() => {
    setCount(prevCount => prevCount + 1);
    setCount(prevCount => prevCount + 1);
  }}
/>

useEffect

컴포넌트가 렌더링될 때마다 원하는 작업이 실행되도록 설정할 수 있는 기능이다. useEffect의 첫번째 파라미터로 전달된 함수는 조건을 만족할 때마다 호출되고, 두번째 파라미터로 전달되는 배열로 호출되는 조건을 설정할 수 있다.

useEffect(() => {
  console.log(`name: ${name}, email: ${email}\n`);
}, [email]);
//이메일이 변경될 때마다 실행된다

실행조건이 컴포넌트가 마운트 될 때로 설정한다면 아래와 같다

useEffect(() => {
  console.log('\n===== Form Component Mount =====\n');
, []);

언마운트 될 때 실행하려면 실행조건, 첫번째 파라미터를을 빈 배열로 만든다.

useEffect(() => {
  console.log('\n===== Form Component Mount =====\n');
  refName.current.focus();
  return () => console.log('\n===== Form Component Unmount =====\n');
}, []);

useRef

특정 컴포넌트를 선택해야 하는 경우 사용된다(ex. 포커스를 옮길 때)

useRef를 사용할 때 주의할 점이 있다.

  1. 컴포넌트의 ref로 지정하면 생성된 변수에 값이 저장되는 것이 아니라 .current 프로퍼티에 값을 담는 것이다.
  2. useState를 이용한 상태와 달리 useRef내용이 변경되어도 컴포넌트는 다시 렌더링 되지 않는다
return (
    <>
      <StyledText>Name: {name}</StyledText>
      <StyledText>Email: {email}</StyledText>
      <StyledTextInput
        value={name}
        onChangeText={text => setName(text)}
        placeholder="name"
        ref={refName}
        returnKeyType="next"
        onSubmitEditing={() => refEmail.current.focus()}
      />
      <StyledTextInput
        value={email}
        onChangeText={text => setEmail(text)}
        placeholder="email"
        ref={refEmail}
        returnKeyType="done"
      />
    </>
  );
};

export default Form;

useMemo

동일한 반복 수행을 제거하여 성능을 최적화시킨다. 첫번째 파라미터에는 함수를 전달하고 두번째 파라미터에는 함수 실행 조건을 배열로 전달한다.

import React, { useState, useMemo } from 'react';
import styled from 'styled-components/native';
import Button from './Button';

const StyledText = styled.Text`
  font-size: 24px;
`;

const getLength = text => {
  console.log(`Target Text: ${text}`);
  return text.length;
};

const list = ['JavaScript', 'Expo', 'Expo', 'React Native'];

let idx = 0;
const Length = () => {
  const [text, setText] = useState(list[0]);

  const _onPress = () => {
    ++idx;
    if (idx < list.length) setText(list[idx]);
  };
  const length = useMemo(() => getLength(text), [text]);
	/*사용하지 않으면 값의 변경이 없더라도 계속 같은 문자열의 길이를 구한다.*/
  return (
    <>
      <StyledText>Text: {text}</StyledText>
      <StyledText>Length: {length}</StyledText>
      <Button title="Get Length" onPress={_onPress} />
    </>
  );
};

export default Length;

커스텀 Hooks 만들기

특정 API에 GET요청을 보내고 응답을 받는 함수를 만들어보자. 이번에는 Fetch를 이용하여 uesFetch라는 이름의 Hook을 만들 것이다.

hooks/useFetch.js

import { useState, useEffect } from 'react';

export const useFetch = url => {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [inProgress, setInProgress] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setInProgress(true);
        const res = await fetch(url);
        const result = await res.json();
        if (res.ok) {
          setData(result);
          setError(null);
        } else {
          throw result;
        }
      } catch (error) {
        setError(error);
      } finally {
        setInProgress(false);
      }
    };
    fetchData();
  }, []);

  return { data, error, inProgress };
};

📝  실행 흐름 요약

  1. 컴포넌트가 처음 렌더링되면 useEffect가 실행된다.
  2. fetchData 함수가 호출되고 inProgress가 true로 바뀐다.
  3. fetch(url)을 실행하고 응답을 JSON으로 변환한다.
  4. 응답이 성공(res.ok)이라면 data에 결과를 저장하고 error를 null로 만든다.
  5. 응답이 실패하면 throw되어 catch로 넘어가고 error 상태에 저장된다.
  6. try/catch가 끝나면 finally가 실행되어 inProgress가 false로 바뀐다.
  7. 최종적으로 훅은 data, error, inProgress 값을 반환한다.

components/Dogs.js

import React from 'react';
import styled from 'styled-components/native';
import { useFetch } from '../hooks/useFetch';

const StyledImage = styled.Image`
  background-color: #7f8c8d;
  width: 300px;
  height: 300px;
`;
const ErrorMessage = styled.Text`
  font-size: 18px;
  color: #e74c3c;
`;
const LoadingMessage = styled.Text`
  font-size: 18px;
  color: #2ecc71;
`;

const URL = 'https://dog.ceo/api/breeds/image/random';
const Dog = () => {
  const { data, error, inProgress } = useFetch(URL);

  return (
    <>
      {inProgress && (
        <LoadingMessage>The API request is in progress</LoadingMessage>
      )}
      <StyledImage source={data?.message ? { uri: data.message } : null} />
      <ErrorMessage>{error?.message}</ErrorMessage>
    </>
  );
};

export default Dog;

📝 실행 흐름 요약

  1. Dog 컴포넌트가 렌더링되면 useFetch(URL)이 호출된다.
  2. useFetch 내부의 useEffect가 실행되어 API 요청이 시작되고 inProgress가 true가 된다.
  3. fetch로 강아지 랜덤 이미지 API를 호출하고 JSON 데이터를 받아온다.
  4. 요청이 성공하면 data에 응답이 저장되고 error는 null이 된다.
  5. 요청이 실패하면 error에 에러 정보가 저장된다.
  6. 요청이 끝나면 inProgress가 false로 바뀐다.
  7. Dog 컴포넌트는 상태에 따라 다음을 렌더링한다:
    • inProgress가 true일 때 로딩 메시지를 표시한다.
    • data.message가 있으면 해당 url의 이미지를 StyledImage에 표시한다.
    • error가 있으면 ErrorMessage로 에러 메시지를 표시한다.

0개의 댓글