일단 오류는 이렇습니다 ㅠ
먼저 구현 하려는 기능은 통신해서 가져온 데이터를 합쳐 랜더링 시키는 것인데요(무한스크롤)
키프롭이 중복된다고 오류가 뜨네요… 분명 중복되는 데이터는 없다는 것을 확인했는데 어떤 코드가 잘못된 것인지
제가 리덕스를 잘못 이해하고 있는 것인지 …로컬스토리지를 이해 못한 것인지 ㅠㅠ 시간 나시면 한번 봐주실 수
있을까요? ㅠㅠ 신규는 이미지 한개만 올릴 수 있다해서 코드로 올리겠습니다 ㅠ
일단 골격은 앱에서 서치바와 갤러리 컨테이너 랜더링
const [searchState, setSearchState] = useState(false);
// searchState의 값에 따라 메인 화면과 검색결과 화면의 전환이 이루어짐.
const [userInfo, setUserInfo] = useState({
isLogin: false,
accessToken: ''
});
const getToken = async (authorizationCode) => {
const { access_token } = await utils.getAccessToken(authorizationCode);
if(!access_token) return
setUserInfo({
...userInfo,
isLogin: true,
accessToken: access_token
})
localStorage.setItem('access_token', access_token);
window.history.replaceState({}, null, window.location.pathname)
}
useEffect(() => {
const url = new URL(window.location.href);
const authorizationCode = url.searchParams.get('code');
const token = localStorage.getItem('access_token');
if(token) {
setUserInfo({
...userInfo,
isLogin: true,
accessToken: token
})
return;
}
if(!authorizationCode && !token) return
if(authorizationCode) {
getToken(authorizationCode)
return
}
}, [])
return (
<Container>
<Header userInfo={userInfo} setUserInfo={setUserInfo}/>
<Search setSearchState={setSearchState} />
<Gallery searchState={searchState} />
</Container>
)
여기에서는 검색 인풋에 값이 있고 검색버튼 누르면 그에 따른 결과를 보여 줍니다 여기에 무한스크롤 관련이랑
api호출 관련 로직이 있습니다.
const Gallery = ({ apiData, searchState }) => {
const { loading, data, error } = useSelector(state => state.dataReducer);
const dispatch = useDispatch();
// const [hasNext, setHasNext] = useState(true);
const getMoreImgEl = useRef(null);
const intersecting = useInfiniteScroll(getMoreImgEl);
const [page, setPage] = useState(1);
// console.log(page)
useEffect(()=> {
dispatch(getImgs(page));
setPage(page => page + 1);
}, [dispatch, intersecting])
if(loading) return <Loading>Loading.....</Loading>;
if(error) return <Error>Error!!</Error>
return (
<GalleryContainer>
{
searchState ? <SearchCard /> : <CardList apiData={data} loading={loading} error={error}/>
}
{
!searchState && <div ref={getMoreImgEl}/>
}
{/* <div ref={getMoreImgEl}/> */}
</GalleryContainer>
)
}
이것은 리덕스 코드 입니다
import axios from "axios";
const REQ_DATA = 'images/REQ_DATA';
const REQ_DATA_SUCCESS = 'images/REQ_DATA_SUCCESS';
const REQ_DATA_ERROR = 'images/REQ_DATA_ERROR';
const initialState = {
loading: false,
data: null,
error: null,
}
export const getImgs = (page) => async (dispatch, getState) => {
const localData = localStorage.getItem('data') || [];
let { storeData } = getState().dataReducer;
const API = `https://api.unsplash.com/photos/?client_id=${process.env.REACT_APP_ACCESS_KEY}&page=${page}&per_page=20`
if(localData.length === 0) {
dispatch({ type: REQ_DATA })
try {
const { data } = await axios.get(API)
const payload = data;
console.log(payload)
localStorage.setItem('data', JSON.stringify(payload))
dispatch({ type: REQ_DATA_SUCCESS, payload })
} catch(e) {
const payload = e;
dispatch({ type: REQ_DATA_ERROR, payload })
}
} else {
const prevData = [...JSON.parse(localData)];
console.log('heoolo?')
console.log(page)
try {
const { data } = await axios.get(API);
console.log(data);
console.log(prevData);
const payload = [...prevData, ...data];
console.log(payload)
dispatch({ type: REQ_DATA_SUCCESS, payload })
} catch(e) {
const payload = e;
dispatch({ type: REQ_DATA_ERROR, payload })
}
}
}
const dataReducer = (state = initialState, action) => {
switch(action.type) {
case REQ_DATA:
return {
...state,
loading: true,
error: null,
}
case REQ_DATA_SUCCESS:
return {
...state,
loading: false,
data: action.payload,
}
case REQ_DATA_ERROR:
return {
...state,
loading: false,
data: null,
error: action.payload
}
default:
return state;
}
}
혹시 몰라 무한스크롤 관련 코드도 올립니다 .
import { useEffect, useState, useRef, useCallback } from "react";
const useInfiniteScroll = (targetEl) => {
const observerRef = useRef(null);
const [intersecting, setIntersecting] = useState(false);
// const observer = new IntersectionObserver(entries => setIntersecting(entries.some(entry => entry.isIntersecting)));
const getObserver = useCallback(() => {
if(!observerRef.current) {
observerRef.current = new IntersectionObserver(entries => setIntersecting(entries.some(entry => entry.isIntersecting)));
}
return observerRef.current;
}, [observerRef.current])
useEffect(() => {
if(targetEl.current) {
getObserver().observe(targetEl.current);
}
return () => {
getObserver().disconnect();
}
}, [getObserver, targetEl.current]);
return intersecting;
}
export default useInfiniteScroll;