Web Project/market (client)

Building "My Listed Products" Screen in React Native

hmmmmmmmmmmmm 2025. 5. 11. 19:13

 

사용자가 판매를 위해 등록한 상품 관리

 

 

 

 

전체 코드

import AppHeader from "@components/AppHeader";
import { NavigationProp, useNavigation } from "@react-navigation/native";
import BackButton from "@ui/BackButton";
import ProductImage from "@ui/ProductImage";
import size from "@utils/size";
import { runAxiosAsync } from "app/api/runAxiosAsync";
import { ProfileNavigatorParamList } from "app/navigator/ProfileNavigator";
import { getListings, Product, updateListings } from "app/store/listings";
import useClient from "hooks/useClient";
import { useEffect, useState } from "react";
import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
import { useDispatch, useSelector } from "react-redux";

type ListingResponse = {
  products: Product[];
};

export default function Listings() {
  const { navigate } = useNavigation<NavigationProp<ProfileNavigatorParamList>>();
  const { authClient } = useClient();

  const dispatch = useDispatch();
  const listings = useSelector(getListings);

  const [fetching, setFetching] = useState(false);

  const fetchListings = async () => {
    setFetching(true);
    const res = await runAxiosAsync<ListingResponse>(
      authClient.get("/product/listings")
    );
    setFetching(false);
    if (res) {
      dispatch(updateListings(res.products));
    }
  };

  useEffect(() => {
    fetchListings();
  }, []);

  return (
    <>
      <AppHeader backButton={<BackButton />} />
      <View style={styles.container}>
        <Text style={styles.title}>My Products</Text>
        <FlatList
          refreshing={fetching}
          onRefresh={fetchListings}
          data={listings}
          contentContainerStyle={listings.length === 0 ? styles.emptyList : styles.flatList}
          keyExtractor={(item) => item.id}
          ListEmptyComponent={
            <Text style={styles.emptyText}>You haven't listed any products yet.</Text>
          }
          renderItem={({ item }) => (
            <Pressable
              onPress={() => navigate("SingleProduct", { product: item })}
              style={styles.listItem}
            >
              <ProductImage uri={item.thumbnail} />
              <Text style={styles.productName} numberOfLines={2}>{item.name}</Text>
              <Text style={styles.productPrice}>₩ {item.price.toLocaleString()}</Text>
            </Pressable>
          )}
        />
      </View>
    </>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: size.padding,
  },
  title: {
    fontSize: 24,
    fontWeight: "bold",
    marginBottom: 15,
  },
  flatList: {
    paddingBottom: 20,
  },
  emptyList: {
    flexGrow: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  listItem: {
    paddingBottom: size.padding,
  },
  productName: {
    fontWeight: "700",
    fontSize: 20,
    letterSpacing: 1,
    paddingTop: 10,
  },
  productPrice: {
    fontSize: 16,
    color: "#666",
    paddingTop: 4,
  },
  emptyText: {
    fontSize: 16,
    color: "#999",
  },
});

 

 

 

코드 상세 설명

 

1. TypeScript 타입 정의

type ListingResponse = {
  products: Product[];
};

 

서버로부터 받는 응답 데이터의 타입을 정의

상품 정보를 담고 있는 객체의 구조 정의

 

 

2. 데이터 Fetching 함수

const fetchListings = async () => {
  setFetching(true);
  const res = await runAxiosAsync<ListingResponse>(
    authClient.get("/product/listings")
  );
  setFetching(false);
  if (res) {
    dispatch(updateListings(res.products));
  }
};

 

비동기 함수로 서버에서 상품 목록을 가져오기:

  1. 로딩 상태를 true로 설정
  2. API 호출 실행
  3. 로딩 상태를 false로 설정
  4. 응답이 성공적이면 Redux 스토어 업데이트

 

 

3. useEffect Hook

useEffect(() => {
  fetchListings();
}, []);

 

컴포넌트가 마운트될 때 한 번만 실행되어 초기 데이터를 로드

 

 

 

주요 기능

 

1. 데이터 로딩 및 상태 관리:

Redux를 통한 전역 상태 관리와 로컬 상태를 조합하여 효율적인 데이터 관리 구현

 

2. 당겨서 새로고침:

FlatList의 기본 기능을 활용한 사용자 친화적인 데이터 갱신

 

3. 상품 상세 페이지 이동:

각 상품을 탭하면 상세 페이지로 이동