useMemo
- 동일한 연산 반복 수행 제거 => 성능 최적화
- 첫번째 파라미터 = 함수
- 두번째 파라미터 = 함수 실행 조건 => 배열로 전달
JavaScript, Expo, React Native로 배열을 설정하고 버튼을 클릭할 때마다 배열을 순환하며 문자열 길이를 구해보겠다.
그 결과,
버튼 클릭 시 문자열 길이를 구했으며
마지막 문자열 이후에는 더이상 변화가 없기에 길이를 반복해서 구하는 것을 확인했다.
import React, { useState } 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 [length, setLength] = useState('');
const _onPress = () => {
setLength(getLength(text));
++idx;
if (idx < list.length) setText(list[idx]);
};
return (
<>
<StyledText>Text: {text}</StyledText>
<StyledText>Length: {length}</StyledText>
<Button title="Get Length" onPress={_onPress} />
</>
)
}
export default Length
이 코드에서는 동일한 문자열 길이를 계산하고 변화가 없는데도 함수가 호출된다는 아쉬운 점이 존재한다.
이를 해결하기 위해 useMemo를 사용하면 된다.
useMemo를 사용하면 값에 변화가 있는 경우에만 함수가 호출되어
불필요한 중복 연산을 제거할 수 있다.
text 값에 변화가 있을 경우에만 길이를 구하도록 수정해주었다.
따라서 동일한 문자열과 마지막 값 이후 함수가 호출되지 않는 것을 확인할 수 있었다.
const _onPress = () => {
++idx;
if (idx < list.length) setText(list[idx]);
};
const length = useMemo(() => getLength(text), [text]);
커스텀 Hooks
- 사용자 자유 Hook
- 네이티브에서는 네트워크 통신을 위해 Fetch, XMLHttpRequest, WebSocket 지원
- 자주 사용하는 코드를 분리해 깔끔한 코드 정리, 재사용 가능
커스텀 Hook으로 특정 API에 GET 요청을 보내고 응답을 받는 함수를 만들어보려고 한다.
네이티브에서 제공하는 Fetch를 이용해 useFetch Hook을 만들겠다.
import { useEffect, useState } from "react";
export const useFetch = url => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(async () => {
try {
const res = await fetch(url);
const result = await res.json();
if (res.ok) {
setData(result);
setError(null);
} else {
throw result;
}
} catch (error) {
setError(error);
}
}, []);
return (
{ data, error }
)
};
위 코드 처럼
API 주소를 전달받아 요청을 보내고 성공 여부에 따라 데이터를 반환하는 useFetch.jsx를 만들어 주었다.
이제 커스텀 Hook을 이용해서 API를 요청해보겠다.
https://dog.ceo/api/breeds/image/random
위 url을 이용해 강아지 이미지를 불러오는 API를 작성하고 useFetch Hook을 사용해 컴포넌트를 만들었다.
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 URL = 'https://dog.ceo/api/breeds/image/random';
const Dog = () => {
const { data, error } = useFetch(URL);
return (
<>
<StyledImage source={data?.message ? { uri: data.message } : null} />
<ErrorMessage>{error?.message}</ErrorMessage>
</>
)
}
export default Dog
그 결과, 다음과 같은 에러가 발생했다.
Warning: useEffect must not return anything besides a function, which is used for clean-up.
이는 useEffect의 첫번째 파라미터로 비동기 함수를 전달했기 때문에 나타난다.
비동기 함수를 이용할 때는 useEffect에 전달되는
함수 내부에 비동기 함수를 정의하고 사용함으로써 문제를 해결할 수 있다.
따라서 커스텀 Hook인 useFetch에서 useEffect 내부에 fetchData 함수를 정의해 비동기 함수를 정의하고,
정의한 비동기 함수를 호출해줌으로써 문제를 해결했다.
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(url);
const result = await res.json();
if (res.ok) {
setData(result);
setError(null);
} else {
throw result;
}
} catch (error) {
setError(error);
}
};
fetchData();
}, []);
💡 "비동기" 란?
작업을 실행할 때 작업이 완료되지 않더라도 다음 코드 실행하는 방식
즉, 작업이 완료되는 결과를 기다리지 않고 다음 코드를 실행해 시간을 절약하고 병렬적인 작업 처리 가능
사용자의 요청에 빠르게 반응할 수 있도록 하기 위한 설계
● 반응성, 성능 향상
● 작업 시간 단축
● 에러 캐치 및 처리 가능 → 안정성 향상
- 대부분의 비동기 작업은 완료 전에 화면 전체, 특정 버튼들이 사용 불가 상태로 변경됨
- 비동기 동작에서는 선행 작업 마무리 전 추가 요청 들어오지 않도록 하는 것이 좋음
- API 진행상태 추적 => 작업 상태에 따라 화면 구성 변경
마지막으로 작업의 진행 상태를 추적해 화면 구성을 변경해보려고 한다.
inProgress를 useState로 정의해주고
API 요청 시작 전과 완료 후 상태를 변경해 진행 상태를 확인했다.
import { useEffect, useState } 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 }
)
};
{inProgress && (
<LoadingMessage>The API request is in progress</LoadingMessage>
)}
진행 상태를 받아서 상태에 따라 화면이 변경될 수 있도록
API 요청이 아직 로딩되지 않았을 때에는 로딩메세지가 뜨게 만들어 주었다.
'공부 > ReactNative' 카테고리의 다른 글
[ReactNative] 전역 상태 관리, Context API, useContext | 처음 배우는 리액트네이티브 7장 (0) | 2025.01.23 |
---|---|
[ReactNative] useState, useEffect, useRef | 처음 배우는 리액트네이티브 6-1장, 6-2장, 6-3장 (0) | 2025.01.22 |
[ReactNative] To-Do-List 애플리케이션 | 처음 배우는 리액트네이티브 5장 (0) | 2025.01.22 |
[ReactNative] 스타일 | 처음 배우는 리액트네이티브 4장 (0) | 2025.01.21 |
[ReactNative] props, state, event | 처음 배우는 리액트네이티브 3-3장, 3-4장 (0) | 2025.01.16 |