프로젝트 이모저모/HoduMarket 프로젝트

타입스크립트 에러) useParams undefined|string 반환 아직 미해결

Ella Seon 2023. 8. 8. 10:57

0. useParams undefined|string 반환

- useParams 는 undefined|string 을 반환한다.

- useParams 가 어째서 undefined를 반환하는지 궁금해졌다.

챗지피티 선생님께 여쭤봤다. 

1) 매개변수가 경로에 없는 경우: 예를 들어, 경로가 /products/:productId로 정의되어 있지만 사용자가 /products만 방문한 경우, productId는 undefined가 됩니다.
2) 잘못된 경로 정의: 경로 정의를 잘못하여 매개변수를 포함하지 않는 경로에서 useParams를 호출하면, 해당 매개변수는 undefined가 됩니다.
3) TypeScript 타입 정의의 문제: useParams의 반환 타입을 정의하는 TypeScript 타입이 올바르게 설정되지 않았다면, 항상 값이 있어도 TypeScript에서는 undefined로 간주될 수 있습니다.

따라서 코드에서 useParams를 사용할 때는 항상 반환된 매개변수가 undefined인지 확인해야 합니다. 그렇지 않으면 예기치 않은 문제가 발생할 수 있습니다.

 

 

1. 해결과정

- 아래처럼 타입 네로잉을 해줬는데 eslint 에서 에러가 떴다.  

const { productId } = useParams();

  if (!productId) {
    navigate("/*");
    return null; // 혹은 적절한 대체 컴포넌트 렌더링
  }

  const { data } = useFetchProductDetail(productId);

  const cartMutation = useMutation(cartAPI.createCart, {
    onSuccess: () => {
      console.log("성공");
    },
    onError: (error: any) => {
      console.log(error);
    },
  });

  const handlePostCart = async () => {
    if (token) {
      const cartData = {
        token: token,
        product_id: productId,
        quantity: count,
        check: true,
      };
      cartMutation.mutate(cartData);
      navigate("/cart");
    } else {
      dispatch(openModal());
    }
  };
[eslint] src\Pages\ProductDetailPage\ProductDetailPage.tsx Line 28:20: React Hook "useFetchProductDetail" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks Line 30:24: React Hook "useMutation" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks Search for the keywords to learn more about each error.

해석 : React 훅은 컴포넌트의 렌더링 간에 호출 순서가 항상 동일해야 합니다. 조건문, 반복문, 중첩 함수 내에서 훅을 호출하면 호출 순서가 일관되지 않을 수 있습니다. 이러한 일관성의 결여는 예상치 못한 버그를 초래할 수 있습니다. 오류를 해결하기 위해서는 훅을 조건적으로 호출하지 않아야 합니다. 훅은 항상 컴포넌트의 최상위 레벨에서 호출되어야 합니다.

eslint 오류를 해결하기 위해 아래처럼 순서를 이동해주었다.

  const { productId } = useParams();

  const { data } = useFetchProductDetail(productId);

  const cartMutation = useMutation(cartAPI.createCart, {
    onSuccess: () => {
      console.log("성공");
    },
    onError: (error: any) => {
      console.log(error);
    },
  });

  const handlePostCart = async () => {
    if (token) {
      const cartData = {
        token: token,
        product_id: productId,
        quantity: count,
        check: true,
      };
      cartMutation.mutate(cartData);
      navigate("/cart");
    } else {
      dispatch(openModal());
    }
  };

  if (!productId) {
    navigate("/*");
    return null; // 혹은 적절한 대체 컴포넌트 렌더링
  }

 

하지만, 이러면 당연히 타입스크립트 에러가 뜰수밖에 없다. string|undefined에러가 해결되지 않기 때문이다. 

 

 

2. 완전하지 않은 해결 : 타입단언 as , 기본값 설정

- 어쩔수 없이 as string 타입 단언을 해주었다. 위 두 에러를 모두 일으키지 않는 건 타입 단언이다.  하지만, as 를 쓰는 것은 타입스크립트를 쓰는 이유와 위배되는것이기 때문에 지양해야하는데, 편하다보니..쓸수밖에 없었다. 'as'키워드를 사용해서 강제로 타입 변환을 수행하면 타입 안정성을 손상시키기 때문에 as로 단언하기 보다는 undefined를 잘 네로잉 할 수 있어야 하는데..방법을 모르겠다. 

 

 

- as 가 아니라 기본값을 설정해주었다. 사실 기본값 설정이나 as 나 별반 차이 없다고 생각한다. 어차피 둘다 강제하는 것이기 때문이다. 기본값이 실제로 의미가 없거나 잘못된 데이터를 가져올수 있기 때문이다. 

- 하지만, 기본값이 url parameter로 갈경우 잘못된 경로이기 때문에 404페이지가 뜬다. as 보다는 미리 잘못된 타입을 막을 수 있어서 괜찮지 않을까 싶다. 

const { productId } = useParams();
  const productIdValue = productId || "defaultProductId";

  const { data } = useFetchProductDetail(productIdValue);

  const cartMutation = useMutation(cartAPI.createCart, {
    onSuccess: () => {
      console.log("성공");
    },
    onError: (error: any) => {
      console.log(error);
    },
  });

  const handlePostCart = async () => {
    if (token) {
      const cartData = {
        token: token,
        product_id: productIdValue,
        quantity: count,
        check: true,
      };
      cartMutation.mutate(cartData);
      navigate("/cart");
    } else {
      dispatch(openModal());
    }
  };