useMemo와 removeNullish 함수로 useQuery 리팩토링

LEEJAEJUN·2024년 2월 7일

yanabada

목록 보기
1/3

모든 옵션을 search params와 query key로 관리하면서 생기는 문제

리액트 쿼리로 숙소 목록을 받아오는 훅을 만들어 두고 사용했습니다. 문제는 모든 params를 query key로 관리하게 되면서 생겼습니다. 카테고리, 키워드, 옵션, 날짜, 위도, 경도 등 값이 바뀔 때마다 새로운 목록을 받아와야 했습니다. 여러 곳에서 일어나는 params의 변화를 한 곳에서 대응하기 위해 useState, useEffect, useProducts에 상당히 비효율적인 코드를 선언하게 되었습니다. 목적은 달성했지만 새로운 목록을 받아올 때마다 쌓이는 캐시를 보고있자니 마음이 아팠습니다. 임시방편으로 `gcTime`을 추가해 너무 길게 캐싱하지 않도록 해놓았습니다.

그렇다고 [’products’] 하나의 쿼리키로 관리하자니 옵션이 바뀔 때마다 해당 컴포넌트에 refetch를 달아줘야 하는 상황이 발생했습니다. 게다가 한 번씩 제대로 작동하지 않는 경우도 있었습니다. 어찌저찌 생각해낸 방법이 지금의 방법입니다.

const ProductList = () => {
  const [searchParams] = useSearchParams();
  const [queryParams, setQueryParams] = useState({
    options: searchParams.getAll("options"),
    order: searchParams.get("order"),
    category: searchParams.get("category"),
    keyword: searchParams.get("keyword"),
    checkInDate: searchParams.get("checkInDate"),
    checkOutDate: searchParams.get("checkOutDate"),
    swx: Number(searchParams.get("swx")),
    swy: Number(searchParams.get("swy")),
    nex: Number(searchParams.get("nex")),
    ney: Number(searchParams.get("ney"))
  });

  useEffect(() => {
    setQueryParams({
      options: searchParams.getAll("options"),
      order: searchParams.get("order"),
      category: searchParams.get("category"),
      keyword: searchParams.get("keyword"),
      checkInDate: searchParams.get("checkInDate"),
      checkOutDate: searchParams.get("checkOutDate"),
      swx: Number(searchParams.get("swx")),
      swy: Number(searchParams.get("swy")),
      nex: Number(searchParams.get("nex")),
      ney: Number(searchParams.get("ney"))
    });
  }, [searchParams]);

  const {
    data: products,
    hasNextPage,
    isFetching,
    fetchNextPage
  } = useProducts({
    ...(queryParams.options && { options: queryParams.options as Option[] }),
    ...(queryParams.order && { order: queryParams.order as OrderState }),
    ...(queryParams.category && { category: queryParams.category as Category }),
    ...(queryParams.checkInDate && { checkInDate: queryParams.checkInDate }),
    ...(queryParams.checkOutDate && { checkOutDate: queryParams.checkOutDate }),
    ...(queryParams.keyword && { keyword: queryParams.keyword }),
    ...(queryParams.swx && { smallX: queryParams.swx }),
    ...(queryParams.swy && { smallY: queryParams.swy }),
    ...(queryParams.nex && { bigX: queryParams.nex }),
    ...(queryParams.ney && { bigY: queryParams.ney }),
    size: 10
  });

useState + useEffect에서 useMemo로 리팩토링

const queryParams = useMemo(() => {
    const params = new URLSearchParams(searchParamString);

    return {
      options: params.getAll("options") as Option[],
      order: params.get("order") as OrderState,
      category: params.get("category") as Category,
      keyword: params.get("keyword"),
      checkInDate: params.get("checkInDate"),
      checkOutDate: params.get("checkOutDate"),
      smallX: Number(params.get("smallX")),
      smallY: Number(params.get("smallY")),
      bigX: Number(params.get("bigX")),
      bigY: Number(params.get("bigY"))
    };
}, [searchParamString]);

removeNullish 함수로 코드 리팩토링

useProducts의 인자가 ...(object.key && key: object.key) 로 반복되면서 엄청나게 길어졌습니다. 멘토님의 의견에 따라 removeNullish 라는 함수를 만들어 보기로 했고 결과는 아래와 같습니다.

const removeNullish = <T extends object>(
  obj: T,
  ...rest: Array<Record<string, any>>
): Partial<T> => {
  const result = Object.assign({}, ...rest);

  Object.entries(obj).forEach(([key, value]) => {
    if (value !== null && value !== undefined && value !== "" && value !== 0) {
      result[key] = value;
    }
  });

  return result;
};

const { data: products } = useProducts(
  removeNullish<Partial<GetProductsRequestParams>>(queryParams, { size: 10 })
);
profile
always be fresh

0개의 댓글