위치 정보를 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} />;
})}
캘린더 라이브러리 중 react-calender를 활용해서 예약 기능을 구현했다. 공식문서에도 잘 나와있지만 영어이기도 하고, 다음에 또 활용할 때 참고하고자 정리를 하려고 한다.
캘린더의 기본 세팅은 우선 연도 이동 버튼을 없애고, 오늘 날짜를 기준으로 한달까지만 선택이 가능하게 만들어주었다. 이때 날짜 계산과 관련된 것은 moment 라이브러리를 사용해주었다.
const [value, onChange] = useState(now);
<Calendar
onChange={onChange}
value={value}
next2Label={null}
prev2Label={null}
maxDate={maxDate}
minDate={now}
/>
그리고 예약된 시간들을 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>
))}
위에서 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);
}}>