[Main Project] UDog / 구현하기 - 중복 키 값 에러, 예약 기능 구현

soohyunee·2023년 3월 25일
0

[Main Project] UDog

목록 보기
13/18

1. 구현하기

진행 상황

  • 버그 수정
  • 비밀번호 변경 구현

진행 예정

  • 화면정의서 정리

2. TIL

2-1. 중복 키 값 에러

Encountered two children with the same key

위치 정보를 geolocation으로 받아와서 loaded가 false라면 기본값을 특정 좌표로 지정해두었다. 그래서인지 처음엔 기본값 좌표가 먼저 상태에 저장이 되고, 이후에 geolocation으로 받아온 좌표가 다시 저장되었다.

  const lat = location.loaded ? JSON.stringify(location.coordinates.lat) : defaultLat;
  const lng = location.loaded ? JSON.stringify(location.coordinates.lng) : defaultLng;

  useEffect(() => {
    if (location.loaded) {
      dispatch(setLat(lat));
      dispatch(setLng(lng));
    } else {
      dispatch(setLat(defaultLat));
      dispatch(setLng(defaultLng));
    }
  }, [location.loaded]);

하지만 위와 같은 방법을 적용하니까 get 요청이 들어가는 무한스크롤 쪽에서 기존 좌표에서 현재 위치의 좌표를 받으면 get 요청을 해주게끔 useEffect로 종속성 배열에 좌표를 추가해줬어야 했다. 그렇게 되니 현 위치에 맞게 다시 한번 get 요청이 들어갔지만, 그러면서 중복된 키 값이 있다는 오류도 같이 발생하게 되었다.

시도한 방법

여러가지 방법을 시도하다 useEffect로 상태를 관리하는 방법을 시도해보았다.
useEffect를 사용하여 lat와 lng이 변경될 때마다 실행하고, latLng 상태 변수를 업데이트한다.
아래의 if(!latLng)라인은 latLng이 아직 셋팅되지 않았을 때 실행되고, latLng이 null 상태에서 이전에 실행된 useEffect에서 lat와 lng이 변경되었다면, get 요청을 하게된다.
이 방법을 적용하니 get 요청이 바뀐 좌표값으로 한번만 일어나서 키 값 중복문제는 해결된 듯 보였으나 위치 정보를 껐을 때는 무한 로딩이 발생하는 문제가 생겼다.

  const [latLng, setLatLng] = useState(null);


  useEffect(() => {
    if (!lat || !lng) {
      return;
    }
    setLatLng({ lat, lng });
  }, [lat, lng]);


    dispatch(setLoading(true));

    if (!latLng) {
      return;
    }

해결 방법

우선 undefined 문제도 발견 되어 좌표를 관리하는 리덕스의 초기값을 넣어주었다. 그리고 위치정보가 로드 될 때 useEffect로 다시 렌더링 시켜 상태를 업데이트 시켜주었다.

const initialState = {
  lat: defaultLat,
  lng: defaultLng,
  address: '',
};
  useEffect(() => {
    if (location.loaded) {
      const lat = JSON.stringify(location.coordinates.lat);
      const lng = JSON.stringify(location.coordinates.lng);
      dispatch(setLat(lat));
      dispatch(setLng(lng));
    }
  }, [location.loaded]);

그리고 구글링을 해보니 키 값을 고유하게 만들기 위해 index 값을 활용하기도 한다고 나왔다. 그래서 map 부분에서 키 값을 미용실의 id 대신 index를 넣어주니 해당 에러는 사라졌다.

 {data.map((shop, index) => {
    return <HairshopList shop={shop} key={index} last={index === shop.length - 1} />;
 })}

2-2. 예약 기능 구현

react-calender 기본 세팅

캘린더 라이브러리 중 react-calender를 활용해서 예약 기능을 구현했다. 공식문서에도 잘 나와있지만 영어이기도 하고, 다음에 또 활용할 때 참고하고자 정리를 하려고 한다.
캘린더의 기본 세팅은 우선 연도 이동 버튼을 없애고, 오늘 날짜를 기준으로 한달까지만 선택이 가능하게 만들어주었다. 이때 날짜 계산과 관련된 것은 moment 라이브러리를 사용해주었다.

const [value, onChange] = useState(now);

<Calendar
	onChange={onChange}
    value={value}
    next2Label={null}
    prev2Label={null}
    maxDate={maxDate}
    minDate={now}
/>

예약된 시간 disabled 처리

그리고 예약된 시간들을 get 요청으로 받아와서 map으로 예약된 시간을 배열로 만들어주었다. 그리고 시간 옵션들 중에 예약된 시간 배열에 include 된 시간들을 disabled 해주었다.

const timeData = useFetch(`${RESERVATION_ENDPOINT}/${id}?select-date=${formatDate}`);
const bookedTimes = timeData?.map((reservation) => reservation.reserveTime);

{TimeOption.map((time, idx) => (
  <S.TimeItem key={idx}>
   	<S.TimeButton
    disabled={bookedTimes?.includes(time)}
    onClick={() => {
 	dispatch(setTime(time));
    setIsopen(false);
  }}>
  	{time}
  </S.TimeButton>
 </S.TimeItem>
))}

지난 시간 disabled 처리

위에서 get 요청은 단지 예약된 시간만을 보여주어서 현재 시간을 기준으로 시간이 지난 옵션도 선택할 수 없게 구현해야했다. 그래서 날짜 계산을 위해 moment 라이브러리를 활용하였다. 시간 옵션을 선택했을 때 hour로 시간 옵션의 시간과 minute으로 분을 가져와서 지금 시간과 비교해서 지났거나 이미 예약된 시간에 속한다면 disabled 할 수 있게 로직을 수정해주었다.

  const disabledTime = (time) => {
    const selectedDateTime = moment(value)
      .hour(time.split(':')[0])
      .minute(time.split(':')[1])
      .toDate();
    return selectedDateTime < now || bookedTimes?.includes(time);
  };


  <S.TimeButton
    disabled={disabledTime(time)}
    onClick={() => {
    dispatch(setTime(time));
    setIsopen(false);
  }}>
profile
FrontEnd Developer

0개의 댓글