-
Notifications
You must be signed in to change notification settings - Fork 2
redux로 메인 피드의 data 를 관리해보자.
jominji edited this page Dec 13, 2019
·
2 revisions
데이터를 redux로 글로벌하게 기억하는 하는 메인 피드 infinite scroll을 만들어보자.
- data는 redux에서 global하게 관리되는 배열이다.
- useGetFeedWallpaperList는 커스텀 훅이다.
skippedNum
은 state로 관리되는 변수이다.fixedNum
은 useRef로 관리되는 변수이다. 9라는 값을 가지고 있다. (ex,몽고디비에서 0개를 skip하고 9개를 가져온다, 몽고디비에서 9개를 skip하고 9개를 가져온다 등등 ) - 맨 처음 render 되면 scroll이벤트를 달아준다.
- skippedNum이 변경되면 doFetch() 함수를 실행한다. doFetch()는 url state를 변경시키는 setUrl() 함수이다.
- data와 isLoading을 FeedWorks 컴포넌트에 넘겨준다.
const FeedWorkContainer:React.FC = () => {
const data = useSelector((state: RootState) => state.feed.workData);
const [{
isLoading, isError, skippedNum, fixedNum,
}, doFetch, onInsert] = useGetFeedWorkList();
useEffect(() => {
window.addEventListener('scroll', onInsert);
return () => {
window.removeEventListener('scroll', onInsert);
};
}, [skippedNum]);
useEffect(() => {
doFetch(`${API_SERVER}/feed/images/more/${fixedNum.current}/${skippedNum}`);
}, [skippedNum]);
return (
<FeedWorks
data={data}
isLoading={isLoading}
/>
);
};
- url은 API서버에 요청할 주소를 관리하는 state이다.
- skippedNum이 업데이트되면 url이 업데이트되고 url이 변경되면 useEffect가 트리거된다. API 서버에서 데이터를 가지고온다.
- 가져온 데이터를 이용해 redux global의 data를 업데이트한다.
- 그럼, skippedNum은 언제 업데이트되는것일까?
- 사용자가 스크롤을 하면 onInsert() 이벤트 핸들러가 호출된다.
- 이 이벤트 핸들러는 사용자의 위치가 맨 끝인지 아닌지 확인한다.
- 사용자의 위치가 맨 끝이라면 skippedNum 에 + 9를해 업데이트한다.
- 왜 skippedNumG를 redux global로 관리했을까?
- 사용자가 스크롤을 열심히 해서 27개의 데이터가 렌더되었다고 하자.
- 사용자가 아이템을 클릭해 상세 페이지를 이동하고 다시 뒤로가기 버튼을 눌렀다면?
- data 가 만약 global하게 관리되지 않았다면 unmount 되면서 data가 사라지고 27개가 아닌 9개 초기 데이터가 보이게된다.
- data 를 global하게 관리되기 때문에 그대고 27개의 데이터를 볼 수 있고
- skippedNumG 또한 global로 관리되기 때문에 맨 밑으로 스크롤을 하게된다면 27에서 +9 가 된 36으로 업데이트된다.
const useFetch = ()
:[{isLoading:boolean, isError:boolean, fixedNum:React.MutableRefObject<number>, skippedNum:number}, (url:string)=>void, ()=>void] => {
const [url, setUrl] = useState('');
...
const fixedNum = useRef(9);
const dispatch = useDispatch();
const skippedNumG = useSelector((state: RootState) => state.feed.workSkippedNum);
const [skippedNum, setSkippedNum] = useState(skippedNumG);
const isLoading2 = useRef(false);
const onInsert = () => {
...
if ((scrollTop + clientHeight) === scrollHeight) {
if (isLoading2.current === false && skippedNumG >= skippedNum) {
setSkippedNum(skippedNum + 9);
}
}
};
useEffect(() => {
const fetchData = async () => {
...
isLoading2.current = true;
try {
const result = await axios(url);
...
const images = result.data.data;
dispatch(getWorkDataMore(images));
isLoading2.current = false;
...
...
fetchData();
}, [url]);
return [{
isLoading, isError, fixedNum, skippedNum,
}, setUrl, onInsert];
};
- data와 isLoading을 props 로 넘겨받아 rendering 한다.
const FeedWorks:React.FC<FeedWorksProps> = ({
data, isLoading,
}) => (
<S.Container>
<S.FeedWrapper>
{
data.map(({
_id, ownerId, url, creator, title, numOfComments, views,
}) => (
<WorksCard
......
- more파라미터는 추가로 요청한 (axios) 데이터를 받는다.
- action에서는 payload라는 key값으로 more에 접근할 수 있다.
import { IImage } from './types';
export const WORK_DATA_MORE = 'upload/WORK_DATA_MORE' as const;
export const getWorkDataMore = (more: IImage[]) => ({
type: WORK_DATA_MORE,
payload: more,
});
- 이전의 상태값에 새로운 상태값을 덮어 씌운다.
...
const initialState: FeedState = {
workData: [],
workSkippedNum: 0,
};
function feed(state:FeedState = initialState, action:FeedAction) {
switch (action.type) {
case WORK_DATA_MORE:
return {
...state,
workData: [...state.workData, ...action.payload],
workSkippedNum: state.workData.length + action.payload.length,
};
default:
return state;
}
}